Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson deserialize JSON array with multiple object types

Tags:

java

json

gson

I have some odd JSON like:

[
  {
    "type":"0",
    "value":"my string"
  },
  {
    "type":"1",
    "value":42
  },
  {
    "type":"2",
    "value": {
    }
  }
]

Based on some field, the object in the array is a certain type. Using Gson, my thought is to have a TypeAdapterFactory that sends delegate adapters for those certain types to a TypeAdapter, but I'm hung up on understanding a good way of reading that "type" field to know which type to create. In the TypeAdapter,

Object read(JsonReader in) throws IOException {
  String type = in.nextString();
  switch (type) {
    // delegate to creating certain types.
  }
}

would assume the "type" field comes first in my JSON. Is there a decent way to remove that assumption?

like image 992
Eric Cochran Avatar asked Apr 21 '16 21:04

Eric Cochran


People also ask

How to use jsonserializer with Gson?

The JsonSerializer interface looks like this: After creating custom serializer for Json, we will also need to register this serializer through GsonBuilder.registerTypeAdapter (Type, Object). Gson invokes it’s call-back method serialize () during serialization when it encounters a field of the specified type.

Can Gson map arrays and lists?

Welcome back to another blog post in our Gson series. After reviewing the basics of Gson, model annotations and mapping of nested objects, we'll go on to a core feature: mapping of Arrays and Lists. Almost every data model out there utilizes some form of list. Fortunately, Gson makes it really easy to deal with them.

Can Gson create a nested user address?

Yes, Gson sorted the fields alphabetically again, but otherwise the result is exactly as we've expected. Gson correctly created the nested userAddress JSON object. Of course, we could add more nested objects for the user's payment method or work address. Even the nested objects can have nested objects!

Is Gson sorting the fields alphabetically?

Yes, Gson sorted the fields alphabetically again, but otherwise the result is exactly as we've expected. Gson correctly created the nested userAddress JSON object. Of course, we could add more nested objects for the user's payment method or work address.


1 Answers

Here is some code I wrote to handle an array of NewsFeedArticle and NewsFeedAd items in Json. Both items implement a marker interface NewsFeedItem to allow me to easily check if the TypeAdater should be used for a particular field.

public class NewsFeedItemTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (!NewsFeedItem.class.isAssignableFrom(type.getRawType())) {
            return null;
        }

        TypeAdapter<JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class);
        TypeAdapter<NewsFeedArticle> newsFeedArticleAdapter = gson.getDelegateAdapter(this, TypeToken.get(NewsFeedArticle.class));
        TypeAdapter<NewsFeedAd> newsFeedAdAdapter = gson.getDelegateAdapter(this, TypeToken.get(NewsFeedAd.class));

        return (TypeAdapter<T>) new NewsFeedItemTypeAdapter(jsonElementAdapter, newsFeedArticleAdapter, newsFeedAdAdapter).nullSafe();
    }

    private static class NewsFeedItemTypeAdapter extends TypeAdapter<NewsFeedItem> {

        private final TypeAdapter<JsonElement> jsonElementAdapter;
        private final TypeAdapter<NewsFeedArticle> newsFeedArticleAdapter;
        private final TypeAdapter<NewsFeedAd> newsFeedAdAdapter;

        NewsFeedItemTypeAdapter(TypeAdapter<JsonElement> jsonElementAdapter,
                TypeAdapter<NewsFeedArticle> newsFeedArticleAdapter,
                TypeAdapter<NewsFeedAd> newsFeedAdAdapter) {
            this.jsonElementAdapter = jsonElementAdapter;
            this.newsFeedArticleAdapter = newsFeedArticleAdapter;
            this.newsFeedAdAdapter = newsFeedAdAdapter;
        }

        @Override
        public void write(JsonWriter out, NewsFeedItem value) throws IOException {
            if (value.getClass().isAssignableFrom(NewsFeedArticle.class)) {
                newsFeedArticleAdapter.write(out, (NewsFeedArticle) value);

            } else if (value.getClass().isAssignableFrom(NewsFeedAd.class)) {
                newsFeedAdAdapter.write(out, (NewsFeedAd) value);

            }

        }

        @Override
        public NewsFeedItem read(JsonReader in) throws IOException {
            JsonObject objectJson = jsonElementAdapter.read(in).getAsJsonObject();

            if (objectJson.has("Title")) {
                return newsFeedArticleAdapter.fromJsonTree(objectJson);

            } else if (objectJson.has("CampaignName")) {
                return newsFeedAdAdapter.fromJsonTree(objectJson);
            }

            return null;
        }
    }
}

You can then register this with Gson using the following code.

return new GsonBuilder()
        .registerTypeAdapterFactory(new NewsFeedItemTypeAdapterFactory())
        .create();
like image 63
Andrew Kelly Avatar answered Sep 27 '22 17:09

Andrew Kelly