oleg.semen
oleg.semen

Reputation: 3061

how do I deserialize array of different objects with GSON

I receive JSON like this

[
  {
    "albums" : [
      {"id":"0", "name":"name"},
      {"id":"1", "name":"name"}
    ],
    "name":"name"
  },
  {
    "tracks" : [
      {"id":"0", "name":"name", "duration":"3:30"},
      {"id":"1", "name":"name", "duration":"2:40"}
    ],
    "name":"name"
  },
  {
    "playlists" : [
      {"id":"0", "name":"name", "tracksCount":"3"},
      {"id":"1", "name":"name", "tracksCount":"40"}
    ],
    "name":"name"
  }
]

Of course I implemented classes Track, Album and Playlist which contains all the fields and classes

Tracks {
  String name;
  List<Track> tracks;
}
Albums {
  String name;
  List<Album> albums;
}
Playlists {
  String name;
  List<Playlist> playlists;
}

I'm trying to deserialize it with:

Gson gson = new Gson();
JsonResponse[] rez = gson.fromJson(str, JsonResponse[].class);

where JsonResponse is

class JsonResponse {
  Albums albums;
  Tracks tracks;
  Playlists playlists;
}

But I get the error:

11-20 19:24:55.210: E/AndroidRuntime(5432): FATAL EXCEPTION: main 11-20 19:24:55.210: E/AndroidRuntime(5432): com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 13 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.internal.bind.ArrayTypeAdapter.read(ArrayTypeAdapter.java:72) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.Gson.fromJson(Gson.java:803) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.Gson.fromJson(Gson.java:768) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.Gson.fromJson(Gson.java:717) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.Gson.fromJson(Gson.java:689) 11-20 19:24:55.210: 11-20 19:24:55.210: E/AndroidRuntime(5432): at android.os.ResultReceiver$MyRunnable.run(ResultReceiver.java:43) 11-20 19:24:55.210: E/AndroidRuntime(5432): at android.os.Handler.handleCallback(Handler.java:587) 11-20 19:24:55.210: E/AndroidRuntime(5432): at android.os.Handler.dispatchMessage(Handler.java:92) 11-20 19:24:55.210: E/AndroidRuntime(5432): at android.os.Looper.loop(Looper.java:130) 11-20 19:24:55.210: E/AndroidRuntime(5432): at android.app.ActivityThread.main(ActivityThread.java:3687) 11-20 19:24:55.210: E/AndroidRuntime(5432): at java.lang.reflect.Method.invokeNative(Native Method) 11-20 19:24:55.210: E/AndroidRuntime(5432): at java.lang.reflect.Method.invoke(Method.java:507) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625) 11-20 19:24:55.210: E/AndroidRuntime(5432): at dalvik.system.NativeStart.main(Native Method) 11-20 19:24:55.210: E/AndroidRuntime(5432): Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 13 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:374) 11-20 19:24:55.210: E/AndroidRuntime(5432): at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:165) 11-20 19:24:55.210: E/AndroidRuntime(5432): ... 21 more

P.S. Some Items in json response might be missed. For example

[
  {
    "albums" : [
      {"id":"0", "name":"name"},
      {"id":"1", "name":"name"}
    ],
    "name":"name"
  }
 ]

So what is the problem and how can I deserialize this JSON?

BTW I can't change JSON. So I need a code that parse it as it is.

Upvotes: 4

Views: 5470

Answers (5)

danipenaperez
danipenaperez

Reputation: 693

Recently I used RuntimeTypeAdapterFactory for the same purpose, and it is easy an clean solution. It is a open Gson integration (extra)

Check this post to see and active example https://jansipke.nl/serialize-and-deserialize-a-list-of-polymorphic-objects-with-gson/ I imported the raw code from github directly to my project (because the class not owns to the maven dependency directly):

The code https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java

hope Helps

Upvotes: 3

oleg.semen
oleg.semen

Reputation: 3061

Thanks everybody for help! Here is my solution.

public class JsonResponse {
    public static class Item {
        String name;
    }

    public static class Tracks extends Item {
        List<Track> tracks;
    }

    public static class Albums extends Item {
        List<Album> albums;
    }

    public static class Playlists extends Item {
        List<Playlist> playlists;
    }

    public static class JsonResponseDeserialize implements JsonDeserializer<List<? extends Item>> {
        Gson gson = new Gson();
        @Override
        public List<? extends Item> deserialize(JsonElement el, Type type, JsonDeserializationContext context) throws JsonParseException {
            List<Item> ls = new ArrayList<Item>();
            JsonArray jarr = el.getAsJsonArray();
            for (JsonElement e : jarr) {
                Item i;
                if (e.getAsJsonObject().get("tracks") != null){
                    i = gson.lsomJson(e, Tracks.class);
                    if (i != null) {
                        ls.add(i);
                        continue;
                    }
                }
                if (e.getAsJsonObject().get("albums") != null){
                    i = gson.lsomJson(e, Albums.class);
                    if (i != null) {
                        ls.add(i);
                        continue;
                    }
                }
                if (e.getAsJsonObject().get("playlists") != null){
                    i = gson.lsomJson(e, Playlists.class);
                    if (i != null) {
                        ls.add(i);
                        continue;
                    }
                }
            }
            return ls;
        }
    }

    private Tracks _tracks;
    private Albums _albums;
    private Playlists _playlists;
}

Deserialization:

private static List<? extends Item> getDatalsomJson(String jsonString) {
    Type type = new TypeToken<List<? extends JsonResponse.Item>>(){}.getType();
    GsonBuilder gb = new GsonBuilder();
    gb.registerTypeAdapter(type, new JsonResponse.JsonResponseDeserialize());
    Gson gson = gb.create();
    List<? extends Item> ls = gson.lsomJson(jsonString, type);
    return ls;
}

Upvotes: 3

Asif Bhutto
Asif Bhutto

Reputation: 3994

Actually you did mistake in JsonResponse class.

Its clear form your json format that its start from an array , and In JsonResponse class you just compose single object not the collection of object . If you look carefully to Gson exception as "BEGIN_OBJECT but was BEGIN_ARRAY". This mean Gason has array of Album,Tracks and PlayList not the object of Albums,Tracks and Playlists class.

Just you need a little change in JsonResponse class as below

private  List<Albums> albums;
private  List<Tracks> tracks;
private  List<Playlists> playlists;

Upvotes: 0

Prabhakaran Ramaswamy
Prabhakaran Ramaswamy

Reputation: 26084

do like this

Your data

    String json = "[{\"albums\":[{\"id\":\"0\",\"name\":\"name\"},{\"id\":\"1\",\"name\":\"name\"}],\"name\":\"name\"},{\"tracks\":[{\"id\":\"0\",\"name\":\"name\",\"duration\":\"3:30\"},{\"id\":\"1\",\"name\":\"name\",\"duration\":\"2:40\"}],\"name\":\"name\"},{\"playlists\":[{\"id\":\"0\",\"name\":\"name\",\"tracksCount\":\"3\"},{\"id\":\"1\",\"name\":\"name\",\"tracksCount\":\"40\"}],\"name\":\"name\"}]";

Your pojos

class JsonResponse {
      ArrayList<Album> albums;
      ArrayList<Track> tracks;
      ArrayList<Playlist> playlists;
      String name;
    @Override
    public String toString() {
        return "JsonResponse [albums=" + albums + ", tracks=" + tracks
                + ", playlists=" + playlists + ", name=" + name + "]";
    }     
}



class Track{
    int id;
    String name;
    String duration;
    @Override
    public String toString() {
        return "Track [id=" + id + ", name=" + name + ", duration=" + duration
                + "]";
    }    
}

class Album{
    int id;
    String name;
    @Override
    public String toString() {
        return "Album [id=" + id + ", name=" + name + "]";
    }   
}

class Playlist{ 
    int id;
    String name;
    int tracksCount;
    @Override
    public String toString() {
        return "Playlist [id=" + id + ", name=" + name + ", tracksCount="
                + tracksCount + "]";
    }

}

here the deserialized array.

   JsonParser parser = new JsonParser();
   JsonArray Jarray = parser.parse(json).getAsJsonArray();
   Gson gson = new Gson();
   for(JsonElement obj : Jarray )
   {
        JsonResponse jsonResponse = gson.fromJson( obj , JsonResponse.class);
        System.out.println(jsonResponse);
   }

Upvotes: 2

Miquel
Miquel

Reputation: 15675

The JSON you have posted does not show an array of JSONResponse objects, instead, it contains JSON Objects of the types Albums, Tracks and Playlists. It would be different if you had this instead:

[
  {
     {
       "albums" : [
         {"id":"0", "name":"name"},
         {"id":"1", "name":"name"}
       ],
       "name":"name"
     },
     {
       "tracks" : [
         {"id":"0", "name":"name", "duration":"3:30"},
         {"id":"1", "name":"name", "duration":"2:40"}
       ],
       "name":"name"
     },
     {
       "playlists" : [
         {"id":"0", "name":"name", "tracksCount":"3"},
         {"id":"1", "name":"name", "tracksCount":"40"}
       ],
       "name":"name"
     }
   }
]

Also, if your objects end up being too complicated, remember you can always implement your own JSONDeserializer class and register it at the GSONBuilder object right before you call create() on it.

Upvotes: 0

Related Questions