3

I'm using Gson successfully to place json into an object. It works like a charm on devices with android 2.2 (emulator and real device), when I deploy to android 4.0 and above (emulator and device) I get this very weird exception.

I've confirmed that there is no problem with json string because the same code runs happily on older devices.

The exception is definitely being thrown here:

Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); return gson.fromJson(jsonString, t);

in the fromJson method.

Any ideas?

Json String: (had to remove the http:// from the urls in the json)

{ "session_id" : "a89d8cd07e356",
    "shoutout_items" : [ { "attachment_url" : "xxxxxx.mobi/pic.php?id=478ba67a44d",
        "attachment_url_full_size" : "xxxxxx.mobi/pic.php?id=01810a5e9f6e7065cd4",
        "expires_at" : 1363264081,
        "have_attachment" : true,
        "message_text" : "Hi",
        "partner" : { "country_code" : "za",
            "gender" : "male",
            "nickname" : "nick",
            "profile_pic_full_size_url" : "xxxxxx.mobi/pic.php?id=810a5e9f6e7065cd43629f1",
            "profile_pic_url" : "xxxxxx.mobi/pic.php?id=23ca67a44d23",
            "profile_summary" : "20,  DBN"
          },
        "shoutout_id" : 31170,
        "type" : "shoutout"
      },
      { "attachment_url" : null,
        "attachment_url_full_size" : null,
        "expires_at" : 1363264081,
        "have_attachment" : false,
        "message_text" : "hello",
        "partner" : { "country_code" : "za",
            "gender" : "male",
            "nickname" : "mark",
            "profile_pic_full_size_url" : "xxxxxx.mobi/pic.php?id=2db9e7f86b9bf7ca",
            "profile_pic_url" : "xxxxxx.mobi/pic.php?id=b110191f1afac",
            "profile_summary" : "40,  DBN"
          },
        "shoutout_id" : 31322,
        "type" : "shoutout"
      }
    ]
}

Stacktrace:

 03-14 14:56:02.200: E/AndroidRuntime(19588): FATAL EXCEPTION: main
    03-14 14:56:02.200: E/AndroidRuntime(19588): java.lang.RuntimeException: Unable to start activity ComponentInfo{mobi.smiggle.android/mobi.smiggle.android.MainFragmentActivity}: java.lang.IllegalArgumentException: class android.text.BoringLayout declares multiple JSON fields named m_paint
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1968)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1993)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.app.ActivityThread.access$600(ActivityThread.java:127)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1159)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.os.Handler.dispatchMessage(Handler.java:99)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.os.Looper.loop(Looper.java:137)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at android.app.ActivityThread.main(ActivityThread.java:4507)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at java.lang.reflect.Method.invokeNative(Native Method)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at java.lang.reflect.Method.invoke(Method.java:511)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at dalvik.system.NativeStart.main(Native Method)
    03-14 14:56:02.200: E/AndroidRuntime(19588): Caused by: java.lang.IllegalArgumentException: class android.text.BoringLayout declares multiple JSON fields named m_paint
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:122)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:72)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.Gson.getAdapter(Gson.java:353)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:82)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:81)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:118)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:72)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.Gson.getAdapter(Gson.java:353)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:82)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:81)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:118)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:72)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.Gson.getAdapter(Gson.java:353)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:82)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:81)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:118)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:72)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.Gson.getAdapter(Gson.java:353)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:82)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:81)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:118)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:72)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.Gson.getAdapter(Gson.java:353)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:82)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:81)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:118)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:72)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.Gson.getAdapter(Gson.java:353)
    03-14 14:56:02.200: E/AndroidRuntime(19588):    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:
NordicElf
  • 31
  • 5
  • 1
    you are apparently trying to deserialize a layout? it seems boringlayout declares a mPaint while layout (which boring layout also extends) also declares a mPaint field. apparently gson cannot handle this case. – njzk2 Mar 14 '13 at 13:05
  • At this point I'm no where near a layout. The code that initiates the call is run in the onCreate of a fragment, I only go into the onCreateView when this returns. I don't have mPaint anywhere in my code. this is why it's weird! – NordicElf Mar 14 '13 at 13:07
  • post the json string. the mPaint(s) both come from android classes BoringLayout and Layout. My guess is your json looks like these classes or others (TextView may be ?) somehow (probably a matter of names) and gson tries to use these classes to deserialize. – njzk2 Mar 14 '13 at 13:16
  • updated my question with json. – NordicElf Mar 14 '13 at 13:31
  • also, what is 't' ? and the end of the stacktrace ? it looks like the end is missing. – njzk2 Mar 14 '13 at 13:52
  • Ummm downgrading Gson from 2.2.2 to 1.7 fixes it. So I know downgrading isn't the right way to solve this - I'm hoping it perhaps offers a clue? Did you spot any conflicting field names in the json? Appreciate any thoughts! – NordicElf Mar 14 '13 at 13:59
  • See [this](http://stackoverflow.com/questions/15756551/solr-java-error-class-com-restfb-types-post-declares-multiple-json-fields-named) – Manoj Kumar Apr 05 '13 at 05:53
  • I used getApplicationContext in my model class that was creating a big problem after reading this post it got solved will it try to serialized that too? – Lochana Ragupathy Jul 30 '13 at 14:19
  • @NordicElf How did you solve this? I am getting same error and spend 2 days but no solution. – Geek Oct 11 '13 at 10:24
  • @Akash Like I said I downgraded my Gson and haven't had an issue since... not cool I know. My code wasn't extending any android views, it was a stand alone object, that has code to create and addviews but that's it. So the supplied answer wasn't helpful. I would really like to know the correct way to handle this. If you come up with an answer please share! – NordicElf Nov 13 '13 at 11:39
  • @NordicElf I think I need to look in detail of your issue to answer. Please create a question with your custom object details such as all variables it has. You can post a link of the question in next comment. – Geek Nov 13 '13 at 12:08
  • @NordicElf you said, that you Object has code to create and addviews, but all fields of your object(if no excludeStrategy) are trying to be serialized, so if any of that is View component, you will face that crash. Please, provide the code of your class. Thanks – OFFmind Dec 03 '13 at 11:16

1 Answers1

2

I've found, that if your instance t extends from any android view component(such as Button, for example) or has field of class, that extends from any android view component, you will get such exception

For some reason, this library can't serialize view components any more. So, you can add exclude strategy. For example, this strategy will exclude all fields, except fields with Annotation @SerializedName:

class Exclude implements ExclusionStrategy {

@Override
public boolean shouldSkipClass(Class<?> arg0) {
    // TODO Auto-generated method stub
    return false;
}

@Override
public boolean shouldSkipField(FieldAttributes field) {
    SerializedName sn = field.getAnnotation(SerializedName.class);
    if(sn != null)
        return false;
    return true;
}}

Suppose we have sample Json:

private final String jsonSample= "{ \"Sample\": { \"field1\":1, \"field2\":2}}";

here is sample code:

 private void parseJson(){
    Exclude ex = new Exclude();
    Gson gson = new GsonBuilder().create();

    GsonObject gObject = gson.fromJson(jsonSample, GsonObject.class);

}

class GsonObject{
    @SerializedName("Sample")
    public Smpl smpl;

//  private Button btn;  <-- Uncomment this line, and you will get your error!

    class Smpl{
        @SerializedName("field1")
        int fl1;
        @SerializedName("field2")
        int fl2;
    }
}

But after adding exclude strategy I've written above:

    Exclude ex = new Exclude();
    Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(ex).addSerializationExclusionStrategy(ex).create();

All works correct.

Finally we has the following test app:

public class MainActivity extends Activity {

private final String jsonSample= "{ \"Sample\": { \"field1\":1, \"field2\":2}}";

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    parseJson();
}

private void parseJson(){
    Exclude ex = new Exclude();
//  Gson gson = new GsonBuilder().create(); <-- without EX strategy it will try to serrialaze Button field in our GsonObject, and throws an exeption
    Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(ex).addSerializationExclusionStrategy(ex).create();

    GsonObject gObject = gson.fromJson(jsonSample, GsonObject.class);

    Toast.makeText(this, "Gson" + gObject.smpl.fl1 + " " + gObject.smpl.fl2, Toast.LENGTH_LONG).show();

}

class GsonObject{
    @SerializedName("Sample")
    public Smpl smpl;

    private Button btn; // <-- this field is our reason of this strange exception on Gson serialization

    class Smpl{
        @SerializedName("field1")
        int fl1;
        @SerializedName("field2")
        int fl2;
    }
}
}

class Exclude implements ExclusionStrategy {

    @Override
    public boolean shouldSkipClass(Class<?> arg0) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        SerializedName ns = field.getAnnotation(SerializedName.class);
        if(ns != null)
            return false;
        return true;
    }

}

p.s. Sorry for my English)

OFFmind
  • 597
  • 1
  • 5
  • 13
  • I am also having issue when try to save object to SharedPreferences using Gson. I read your answer but could not understand it fully. If you want to look at my question then here is the link : http://stackoverflow.com/questions/19296404/save-custom-object-in-shared-preferences Thank you. – Geek Oct 10 '13 at 13:58
  • I tried to understand your code but want to confirm what I mean is correct or not. So my understanding is that I need a class that implements `ExclusionStrategy` with overridden methods. And I need to add `@SerializedName` tag for all vars(other than view component) of my custom object. Am I right? – Geek Oct 10 '13 at 17:32
  • Yes, View components couldn't be serialized, maybe some other components too, so you have to create ExclusionStrategy and serialize ONLY(!) fields you need (you mark it with @SerializedName), so other fields of that class will be ignored by gson serialization. – OFFmind Oct 22 '13 at 10:46