ketan268
ketan268

Reputation: 21

How to parse dynamic json in android with retrofit 2 using annotations

I have a JSON structure which I want to parse using retrofit 2 (@Expose). Below I have mentioned the JSON. Need help to parse it using dynamic annotations.

{
  "status": 1,
  "message": "success",
  "data" : [
    {
      "type": 1,
      "heading": "",
      "description": "",
      "created_on": 141123213,
      "author_id": 123,
      "author_name": "some name",
      "author_pic": "some_pic",
      "read_time": "3.1 min",
      "post_pic_url": "",
      "post_web_url": "",
      "isLiked": false,
      "isSaved": false,
      "totalLikes": 12
   },
   {
      "type": 2,
      "author_id": 123,
      "author_name": "some name",
      "author_pic": "some pic",
      "author_about": "",
      "tags":[
        "travel", "weekends"
      ],
      "isFollowing": false
   },
   {
     "type": 3,
     "poll_name": "Some name",
     "poll_options": [
       "opt1", "opt2", "opt3"
     ],
     "author_id": 123,
     "author_name": "some name",
     "author_pic": "some pic",
     "isLiked": true,
     "isFollowing": false
   },
   {
     "type": 4,
     "ad_url": "url",
     "ad_pic": "pic"
   },
   {
     "type": 5,
     "tags": [
       "tag1", "tag2", "tag3"
     ]
   }
  ]
 }

I have updated the JSON structure with all 5 types.

Upvotes: 2

Views: 1355

Answers (2)

Lyubomyr Shaydariv
Lyubomyr Shaydariv

Reputation: 21115

Retrofit does not do serialization and deserialization, but Gson does. You might want to use RuntimeTypeAdapterFactory from the Google Gson extras package. It's not published at artifact repositories, and you can simply copy the code to your project. If type adapters are somewhat complex (as they work with JSON streams), you might find JsonDeserializer<T> easier to use and probably maintain (they work with JSON trees consuming some more memory, but it's the only way to go here anyway).

Define your mappings similar to:

// There might be no the common root, and target lists might be parameterized with Object, but it's up to you
abstract class Element {

    final int type = Integer.valueOf(0);

    // Since the number of types is really finite, we can define all known types in one place
    private Element() {
    }

    static final class Type1Element
            extends Element {

        // the rest of properties go here

        // Gson does not need constructors, neither we do (at least public ones)
        private Type1Element() {
        }

    }

    static final class Type2Element
            extends Element {

        // the rest of properties go here

        private Type2Element() {
        }

    }

}
final class Response<T> {

    final int status = Integer.valueOf(0);
    final String message = null;
    final T data = null;

}

Now the deserializer itself:

final class ElementJsonDeserializer
        implements JsonDeserializer<Element> {

    private static final JsonDeserializer<Element> elementJsonDeserializer = new ElementJsonDeserializer();

    private ElementJsonDeserializer() {
    }

    // The deserializer is essentially a singleton, but we hide away this fact making sure that only 1 instance exists
    static JsonDeserializer<Element> getElementJsonDeserializer() {
        return elementJsonDeserializer;
    }

    @Override
    public Element deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final int typeCode = jsonElement.getAsJsonObject().getAsJsonPrimitive("type").getAsInt();
        // Simple dispatching here
        // RuntimeTypeAdapterFactory basically does the same
        switch ( typeCode ) {
        case 1:
            return context.deserialize(jsonElement, Type1Element.class);
        case 2:
            return context.deserialize(jsonElement, Type2Element.class);
        default:
            throw new JsonParseException("Unrecognized type: " + typeCode);
        }
    }

}

Now get it all working together (response.json is your JSON document resource):

private static final Type type = new TypeToken<Response<List<Element>>>() {
}.getType();

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Element.class, getElementJsonDeserializer())
        .create();

public static void main(final String... args)
        throws IOException {
    try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q43802350.class, "response.json") ) {
        final Response<List<Element>> response = gson.fromJson(jsonReader, type);
        response.data
                .stream()
                .map(Element::getClass)
                .map(Class::getSimpleName)
                .forEach(System.out::println);
    }
}

Output:

Type1Element
Type2Element

Of course, don't forget to register the gson instance with GsonConverterFactory.create(gson) in your Retrofit builder.

Upvotes: 0

MayurDev
MayurDev

Reputation: 199

1 Use Retrofit convert example GSON convert

2 Add com.squareup.retrofit2:converter-gson in gradle file 3 Add converter factory in Retrofit object

Retrofit retrofit = new Retrofit.Builder() .baseUrl(Ws_Url) .addConverterFactory(GsonConverterFactory.create()) .client(clientBuilder.build()) .build();

4 Create Model class for Your response Use below link to generate model class http://www.jsonschema2pojo.org/

Upvotes: 1

Related Questions