question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

getDelegateAdapter() does not work properly in TypeAdapterFactory used from JsonAdapter annotation

See original GitHub issue

GSON’s getDelegateAdapter method does not appear to work properly inside of a TypeAdapterFactory that is used through the @JsonAdapter annotation. This was found using GSON 2.8.0 and Java 7.

For example, given a class like:

public final class Thing {
    @JsonAdapter(NonEmptyMapAdapterFactory.class)
    private final Map<Integer, String> data;

    public Thing() {
        data = new HashMap<>();
    }
}

And a TypeAdapterFactory like:

public class NonEmptyMapAdapterFactory implements TypeAdapterFactory {

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

        //final TypeAdapter<T> delegate = gson.getAdapter(type);
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);

        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter writer, T value) throws IOException {
                final Map map = (Map) value;
                if (map == null || map.isEmpty()) {
                    writer.nullValue();
                } else {
                    delegate.write(writer, value);
                }
            }

            @SuppressWarnings("unchecked")
            @Override
            public T read(final JsonReader reader) throws IOException {
                final T map = delegate.read(reader);
                if (map == null) {
                    return (T) new HashMap();
                } else {
                    return map;
                }
            }
        };
    }

}

The call to gson.getDelegateAdapter(this, type); returns an adapter of type “ReflectiveTypeAdapterFactory$Adapter”. Calling gson.getAdapter(type); instead does return the expected “MapTypeAdapterFactory$Adapter”, but this obviously won’t work correctly if this adapter factory is instead used without the JsonAdapter annotation.

If the annotation is removed from the field in the Thing class and gson is instead set up like:

new GsonBuilder()
        .registerTypeAdapterFactory(new NonEmptyMapAdapterFactory())
        .create();

Then the custom TypeAdapterFactory shown above works exactly as expected: gson.getDelegateAdapter(this, type); returns a “MapTypeAdapterFactory$Adapter”, but calling gson.getAdapter(type); returns the the same class, resulting in a stack overflow in the read and write methods.

The problem seems to happen because of this code at the beginning of getDelegateAdapter():

// Hack. If the skipPast factory isn't registered, assume the factory is being requested via
// our @JsonAdapter annotation.
if (!factories.contains(skipPast)) {
  skipPast = jsonAdapterFactory;
}

In the list of factories, jsonAdapterFactory comes after the entry for the MapTypeAdapterFactory, causing both to be skipped.

Issue Analytics

  • State:open
  • Created 7 years ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
derthomsncommented, Jul 25, 2017

I was wondering if this bug will be solved soon, since im in an similar spot as @zman0900

Kind regards Thomas

0reactions
somayajcommented, May 9, 2021

Can this be worked?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Adding a custom TypeAdapterFactory for Google GSON to ...
I know i can register TypeAdapters based on field type, however, I would like to be able to use annotations to declare intent...
Read more >
[google-gson] r1305 committed - added a test for ... - Google Groups
+ * work correctly for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}. + */ + public void testSubclassesAutomaticallySerialzed() throws ...
Read more >
JsonAdapter (Gson 2.8.5 API) - Javadoc.io
An annotation that indicates the Gson TypeAdapter to use with a class or field. Here is an example of how this annotation is...
Read more >
Gson (Gson 2.8.8 API) - Javadoc
You can create a Gson instance by invoking new Gson() if the default configuration is all you need. You can also use GsonBuilder...
Read more >
https://raw.githubusercontent.com/google/gson/mast...
If this behavior is not desired, the following workarounds can be used: ... <TypeAdapterFactory>emptyList(), DEFAULT_OBJECT_TO_NUMBER_STRATEGY ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found