Suggestion(s) for deserializing Json objects which has some attributes with a dynamic type with gson

Viewed 56

I want to deserialize the following object:

{
  "qid": "226",
  "parent_qid": "225",
  "sid": "298255",
  "gid": "25",
  "type": "M",
  "title": "SQ001",
  "question": "question text",
  "preg": null,
  "help": null,
  "other": "N",
  "mandatory": null,
  "question_order": "1",
  "language": "de",
  "scale_id": "0",
  "same_default": "0",
  "relevance": "1",
  "modulename": "",
  "available_answers": "No available answers",
  "subquestions": "No available answers",
  "attributes": "No available attributes",
  "attributes_lang": "No available attributes",
  "answeroptions": "No available answer options",
  "defaultvalue": null
}

Members with types:

private HashMap<String, String> available_answers;

private HashMap<Integer, SubQuestion> subquestions;

My problem is that in some cases the attributes "answeroptions" and "available_answers" ( perhaps one or two more) aren't just strings but other objects.

A) I read about creating a custom deserializer to handle such cases but this would lead to the case that I also would have to deserialize the other attributes with static types ( more unnecessary code ).

B) Another approach which came to my mind is that I could "deserialize" the attributes to JsonObjects and deserialize them completely in the corresponding getters.

So my question is if you could suggest me approach a), b) or another one.

Greetings and thanks in advance!

1 Answers

If these fields only have a string JSON value if they have no value, then you could use the @JsonAdapter annotation and a custom TypeAdapterFactory:

/**
 * Only for use on fields annotated with {@code @JsonAdapter}.
 */
public class StringAsNullTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        TypeAdapter<T> delegate = gson.getAdapter(type);
        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, Object value) throws IOException {
                // You will have to implement this in case you are
                // also writing JSON data
                throw new UnsupportedOperationException();
            }
            
            @Override
            public T read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.STRING) {
                    in.skipValue(); // skip the string
                    return null;
                }
                return delegate.read(in);
            }
        };
    }
}

You can then use that TypeAdapterFactory like this:

class Quiz {
    public String qid;
    public String parent_qid;
    
    // ...
    
    @JsonAdapter(StringAsNullTypeAdapterFactory.class)
    public HashMap<String, String> available_answers;
    @JsonAdapter(StringAsNullTypeAdapterFactory.class)
    public HashMap<Integer, SubQuestion> subquestions;
}

You then use your Gson instance as usual, there is no further configuration required.

Related