0

I'm trying to obtain POJO instances using Gson and Retrofit2.

A typical JSON response looks like this.

My issue is with the Infobox field. In some cases, (like this) the field would be an object of the following type and an empty string otherwise.

class Infobox {
    public List<Content> content = new ArrayList<>();
    public List<Metum> meta;
}

class Content {
    public String dataType;
    public String value;
    public String label;
    public Integer wikiOrder;
}

class Metum {
    public String dataType;
    public String value;
    public String label;
}

I tried writing a TypeAdapter as below

class InfoboxAdapter extends TypeAdapter<Infobox> {
    final Gson embedded = new Gson();

    @Override
    public void write(JsonWriter out, Infobox infobox) throws IOException {
        if (infobox == null) {
            out.nullValue();
            return;
        }
        out.beginObject();
        out.name("content");
        embedded.toJson(embedded.toJsonTree(infobox.content), out);

        out.name("meta");
        embedded.toJson(embedded.toJsonTree(infobox.meta), out);
        out.endObject();
    }

    @Override
    public Infobox read(JsonReader in) throws IOException {
        if ("".equals(in.peek())) {
            return null;
        }

        return embedded.fromJson(in, Infobox.class);
    }

But it fails with java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING

The more confusing fact is that the field meta in the response, which is also an object, will in some cases have the value as null (and not an empty string like infobox)

I'd prefer to be able to do it using Gson as I've used it for everything else and I don't want to add another dependency

Pawan
  • 73
  • 3
  • 10

2 Answers2

0

Hi Please go to :http://www.jsonschema2pojo.org/ paste your code. this sites automatically create your all related classes.

if issue please have a look at this link.

my drive link

Md Maidul Islam
  • 2,294
  • 3
  • 17
  • 22
  • I know about this site. As you can see in the question, I already have the POJO to represent Infobox. The issue is that when infobox does not have data, it returns `""` instead of `null`. These are two different datatypes and that trips Gson into throwing exceptions. – Pawan Jul 14 '16 at 08:08
0

I ended up using a JsonDeserializer. Google recommends:

New applications should prefer TypeAdapter, whose streaming API is more efficient than this interface's tree API.

But I didn't notice any performance impact for my use. I might someday rewrite this to use a TypeAdapter, but this works for me in till then

class InfoboxDeserialiser implements JsonDeserializer<Infobox> {

    @Override
    public Infobox deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        try {
            if (json.isJsonNull() || json.isJsonPrimitive()) {
                return null;
            }

            JsonObject jsonObject = json.getAsJsonObject();

            Infobox infobox = new Infobox();

            JsonArray jsonContent = jsonObject.get("content").getAsJsonArray();
            JsonArray jsonMeta = jsonObject.get("meta").getAsJsonArray();

            infobox.content = new Content[jsonContent.size()];
            for (int i = 0; i < jsonContent.size(); i++) {
                infobox.content[i] = context.deserialize(jsonContent.get(i), Content.class);
            }

            infobox.meta = new Metum[jsonMeta.size()];
            for (int i = 0; i < jsonMeta.size(); i++) {
                infobox.meta[i] = context.deserialize(jsonContent.get(i), Metum.class);
            }

            return infobox;
        } catch (Exception e) {
            Timber.e(e, "Failed to deserialise the infobox");
            return null;
        }
    }
}

Where the classes are as follows

class Metum {
    public String dataType;
    public String value;
    public String label;
}

class Content {
    public String dataType;
    public String value;
    public String label;
    public Integer wikiOrder;
}

I register this deserializer while creating the service object

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Infobox.class, new InfoboxDeserialiser());
GsonConverterFactory converterFactory = GsonConverterFactory.create(gsonBuilder.create());
        Retrofit.Builder builder = new Retrofit.Builder()
        .baseUrl("https://api.duckduckgo.com/")
        .addConverterFactory(converterFactory);
Pawan
  • 73
  • 3
  • 10