getDelegateAdapter() does not work properly in TypeAdapterFactory used from JsonAdapter annotation
See original GitHub issueGSON’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:
- Created 7 years ago
- Comments:6 (1 by maintainers)
Top GitHub Comments
I was wondering if this bug will be solved soon, since im in an similar spot as @zman0900
Kind regards Thomas
Can this be worked?