google / gson

A Java serialization/deserialization library to convert Java Objects into JSON and back
Apache License 2.0
23.34k stars 4.28k forks source link

Another approach to fix 2563 #2573

Open msevcenko opened 10 months ago

msevcenko commented 10 months ago

Fixes https://github.com/google/gson/issues/2563

This is alternative, more conservative approach to https://github.com/google/gson/pull/2571 . It doesn't attempt to fix the issue out of the box, but allows user code to intercept type variables.

The trick is the following. ObjectTypeAdapter instance is split into two. The first instance, which cannot be overriden, does not trigger on the mere condition type.getRawType() == Object.class. Specifically, it passes through TypeTokens if TypeToken.getType is a type variable, with a non-Object bound.

There is another instance that maps all remaining TypeTokens with rawType being Object.class, to ObjectTypeAdapter. But this second instance comes only after user defined adapters, so user has a chance to intercept type variables.

You may then fix the issue with the following custom adapter factory:

/**
 * This adapter factory works around the https://github.com/google/gson/issues/2563 gson issue. Unfortunately it must be
 * applied to each affected field using the @JsonAdapter(ResolveGenericBoundFactory.class) declaration, as it is
 * otherwise superseded by the ObjectTypeAdapter.
 * <p>
 * It is also registered with GsonFactory, but is ineffective without annotation, unless PR 2573 is implemented.
 */
public class ResolveGenericBoundFactory implements TypeAdapterFactory  {

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (type.getType() instanceof TypeVariable<?> tv) {
            Type[] bounds = tv.getBounds();
            if (bounds.length == 1 && bounds[0] != Object.class) {
                Type bound = bounds[0];
                return (TypeAdapter<T>) gson.getAdapter(TypeToken.get(bound));
            }
        }
        return null;
    }

}

There is still a change in behavior. Namely, custom type adapters get type variable tokens to process. This may be unexpected for them, they may fail on such an input. Before the change in behavior, custom type adapters never get the Type of TypeVariable, as this was unconditionally handled by the non-overridable ObjectTypeAdapter.

Even more conservative approach may be to introduce new option to GsonBuilder to trigger this new behavior.

Purpose

The purpose is to fix https://github.com/google/gson/issues/2563 as described therein.

Description

See discussion in https://github.com/google/gson/issues/2563. See InferenceFromTypeVariableTest for demo.