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

為{幸葍}努か 提交于 2019-12-18 09:34:13

问题


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.


回答1:


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/




回答2:


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.



来源:https://stackoverflow.com/questions/43802350/how-to-parse-dynamic-json-in-android-with-retrofit-2-using-annotations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!