Search code examples
androidjsongsonretrofitjson-deserialization

How to deserialize JSON field with dynamic type?


In a request to the Reddit API, there is a field associated with each post called edited. This field either has a boolean value or if a post has been edited, has a long value which I think is the timestamp of when the post was edited. How can I deserialize this with GSON without knowing the type? If I try to deserialize into a Boolean value I get an exception if a timestamp is present.

Image below of JSON response:

JSON response


Solution

  • Annotating the dynamic field with @JsonAdapter is probably the easiest way to work around this (assuming you have a DTO class):

    final class Datum {
    
        @JsonAdapter(MaybeLongTypeAdapter.class)
        final Long edited = null;
    
    }
    

    Where MaybeLongTypeAdapter is as follows:

    final class MaybeLongTypeAdapter
            extends TypeAdapter<Long> {
    
        private MaybeLongTypeAdapter() {
        }
    
        @Override
        public void write(final JsonWriter out, final Long value) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public Long read(final JsonReader in)
                throws IOException {
            switch ( in.peek() ) {
            case NULL:
                return null;
            case BOOLEAN:
                if ( in.nextBoolean() ) {
                    throw new JsonSyntaxException("Unexpected `true` at " + in);
                }
                return null;
            case NUMBER:
                return in.nextLong();
            default:
                throw new JsonSyntaxException("Unexpected element at " + in);
            }
        }
    
    }
    

    The type adapter above is pretty self-descriptive. It can be implemented in a more generic way, of course, but it's out of scope here. Additionally, please note that it does not pick the original Long type adapter that can be re-configured in GsonBuilder. Example of use:

    private static final Gson gson = new Gson();
    
    private static final Type listOfDatumType = new TypeToken<List<Datum>>() {}.getType();
    
    public static void main(final String... args) {
        final String json = "[{\"edited\": false},{\"edited\": 1527130582}]";
        final List<Datum> data = gson.fromJson(json, listOfDatumType);
        for ( final Datum datum : data ) {
            System.out.println(datum.edited);
        }
    }
    

    Output:

    null
    1527130582