gson@autovalue和optional< >不一起工作,有解决方法吗?

slsn1g29  于 2021-06-27  发布在  Java
关注(0)|答案(1)|浏览(541)

gson不直接支持序列化@autovalue类或可选的<>字段,但是com.ryanharter.auto.value添加了@autovalue和net。dongliu:gson-java8-datatype adds 可选<>和其他java8类型。
然而,他们一点也不合作。
测试代码:

public class TestOptionalWithAutoValue {
  private static final Gson gson = new GsonBuilder().serializeNulls()
          // doesnt matter which order these are registered in
          .registerTypeAdapterFactory(new GsonJava8TypeAdapterFactory())
          .registerTypeAdapterFactory(AutoValueGsonTypeAdapterFactory.create())
          .create();

  @Test
  public void testAutoValueOptionalEmpty() {
    AvoTestClass subject = AvoTestClass.create(Optional.empty());

    String json = gson.toJson(subject, AvoTestClass.class);
    System.out.printf("Json produced = %s%n", json);
    AvoTestClass back = gson.fromJson(json, new TypeToken<AvoTestClass>() {}.getType());
    assertThat(back).isEqualTo(subject);
  }

  @Test
  public void testAutoValueOptionalFull() {
    AvoTestClass subject = AvoTestClass.create(Optional.of("ok"));

    String json = gson.toJson(subject, AvoTestClass.class);
    System.out.printf("Json produced = '%s'%n", json);
    AvoTestClass back = gson.fromJson(json, new TypeToken<AvoTestClass>() {}.getType());
    assertThat(back).isEqualTo(subject);
  }
}

@AutoValue
public abstract class AvoTestClass {
  abstract Optional<String> sval();

  public static AvoTestClass create(Optional<String> sval) {
    return new AutoValue_AvoTestClass(sval);
  }

  public static TypeAdapter<AvoTestClass> typeAdapter(Gson gson) {
    return new AutoValue_AvoTestClass.GsonTypeAdapter(gson);
  }
}

@GsonTypeAdapterFactory
public abstract class AutoValueGsonTypeAdapterFactory implements TypeAdapterFactory {
  public static TypeAdapterFactory create() {
    return new AutoValueGson_AutoValueGsonTypeAdapterFactory();
  }
}

渐变相关性:

annotationProcessor "com.google.auto.value:auto-value:1.7.4"
    annotationProcessor("com.ryanharter.auto.value:auto-value-gson-extension:1.3.1")
    implementation("com.ryanharter.auto.value:auto-value-gson-runtime:1.3.1")
    annotationProcessor("com.ryanharter.auto.value:auto-value-gson-factory:1.3.1")

    implementation 'net.dongliu:gson-java8-datatype:1.1.0'

失败原因:

Json produced = {"sval":null}
...
java.lang.NullPointerException: Null sval
...

在序列化时调用net.dongliu.gson.optionaladapter,但在反序列化时不调用。
我想知道是否有一个解决办法,或者如果答案是gson需要有可选的直接支持?

lp0sw83n

lp0sw83n1#

很高兴看到您通过添加更多信息甚至添加测试更新了您的问题!:)这真的很清楚!
我不确定,但是生成的类型适配器没有提到 sval :

jsonReader.beginObject();
// [NOTE] This is where it is initialized with null, so I guess it will definitely fail if the `sval` property is not even present in the deserialized JSON object
Optional<String> sval = null;
while (jsonReader.hasNext()) {
String _name = jsonReader.nextName();
// [NOTE] This is where it skips `null` value so it even does not reach to the `OptionalAdapter` run
if (jsonReader.peek() == JsonToken.NULL) {
  jsonReader.nextNull();
  continue;
}
switch (_name) {
  default: {
    if ("sval".equals(_name)) {
      TypeAdapter<Optional<String>> optional__string_adapter = this.optional__string_adapter;
      if (optional__string_adapter == null) {
        this.optional__string_adapter = optional__string_adapter = (TypeAdapter<Optional<String>>) gson.getAdapter(TypeToken.getParameterized(Optional.class, String.class));
      }
      sval = optional__string_adapter.read(jsonReader);
      continue;
    }
    jsonReader.skipValue();
  }
}
}
jsonReader.endObject();
return new AutoValue_AvoTestClass(sval);

我不知道是否有办法配置autovalue或其他生成器中的默认值,但它看起来像一个bug。
如果没有任何方法来解决它(比如说,放弃图书馆开发;等待修复的时间太长;不管怎样),您总是可以自己实现它,但是需要一些运行时成本(基本上这就是gson在数据包对象的后台工作的方式)。这个想法是把工作交给内部人员 RuntimeTypeAdapterFactory 这样它就可以处理一个具体的类,而不是一个抽象类,并根据注册的类型适配器设置所有字段(这样就可以支持java8类型)。这里的代价是反射,因此适配器的工作速度可能比生成的类型适配器慢。另一件事是,如果json属性在json对象中甚至没有遇到,则相应的字段将保留 null . 这需要另一个反序列化后类型适配器。

final class SubstitutionTypeAdapterFactory
        implements TypeAdapterFactory {

    private final Function<? super Type, ? extends Type> substitute;

    private SubstitutionTypeAdapterFactory(final Function<? super Type, ? extends Type> substitute) {
        this.substitute = substitute;
    }

    static TypeAdapterFactory create(final Function<? super Type, ? extends Type> substitute) {
        return new SubstitutionTypeAdapterFactory(substitute);
    }

    @Override
    @Nullable
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        @Nullable
        final Type substitution = substitute.apply(typeToken.getType());
        if ( substitution == null ) {
            return null;
        }
        @SuppressWarnings("unchecked")
        final TypeAdapter<T> delegateTypeAdapter = (TypeAdapter<T>) gson.getDelegateAdapter(this, TypeToken.get(substitution));
        return delegateTypeAdapter;
    }

}
final class DefaultsTypeAdapterFactory
        implements TypeAdapterFactory {

    private final Function<? super Type, ? extends Type> substitute;
    private final LoadingCache<Class<?>, Collection<Map.Entry<Field, ?>>> fieldsCache;

    private DefaultsTypeAdapterFactory(final Function<? super Type, ? extends Type> substitute, final Function<? super Type, ?> toDefault) {
        this.substitute = substitute;
        fieldsCache = CacheBuilder.newBuilder()
                // TODO tweak the cache
                .build(new CacheLoader<Class<?>, Collection<Map.Entry<Field, ?>>>() {
                    @Override
                    public Collection<Map.Entry<Field, ?>> load(final Class<?> clazz) {
                        // TODO walk hieararchy
                        return Stream.of(clazz.getDeclaredFields())
                                .map(field -> {
                                    @Nullable
                                    final Object defaultValue = toDefault.apply(field.getGenericType());
                                    if ( defaultValue == null ) {
                                        return null;
                                    }
                                    field.setAccessible(true);
                                    return new AbstractMap.SimpleImmutableEntry<>(field, defaultValue);
                                })
                                .filter(Objects::nonNull)
                                .collect(Collectors.toList());
                    }
                });
    }

    static TypeAdapterFactory create(final Function<? super Type, ? extends Type> substitute, final Function<? super Type, ?> toDefault) {
        return new DefaultsTypeAdapterFactory(substitute, toDefault);
    }

    @Override
    @Nullable
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
        @Nullable
        final Type substitution = substitute.apply(typeToken.getType());
        if ( substitution == null ) {
            return null;
        }
        if ( !(substitution instanceof Class) ) {
            return null;
        }
        final Collection<Map.Entry<Field, ?>> fieldsToPatch = fieldsCache.getUnchecked((Class<?>) substitution);
        if ( fieldsToPatch.isEmpty() ) {
            return null;
        }
        final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
        return new TypeAdapter<T>() {
            @Override
            public void write(final JsonWriter out, final T value)
                    throws IOException {
                delegateTypeAdapter.write(out, value);
            }

            @Override
            public T read(final JsonReader in)
                    throws IOException {
                final T value = delegateTypeAdapter.read(in);
                for ( final Map.Entry<Field, ?> e : fieldsToPatch ) {
                    final Field field = e.getKey();
                    final Object defaultValue = e.getValue();
                    try {
                        if ( field.get(value) == null ) {
                            field.set(value, defaultValue);
                        }
                    } catch ( final IllegalAccessException ex ) {
                        throw new RuntimeException(ex);
                    }
                }
                return value;
            }
        };
    }

}
@AutoValue
abstract class AvoTestClass {

    abstract Optional<String> sval();

    static AvoTestClass create(final Optional<String> sval) {
        return new AutoValue_AvoTestClass(sval);
    }

    static Class<? extends AvoTestClass> type() {
        return AutoValue_AvoTestClass.class;
    }

}
public final class OptionalWithAutoValueTest {

    private static final Map<Type, Type> autoValueClasses = ImmutableMap.<Type, Type>builder()
            .put(AvoTestClass.class, AvoTestClass.type())
            .build();

    private static final Map<Class<?>, ?> defaultValues = ImmutableMap.<Class<?>, Object>builder()
            .put(Optional.class, Optional.empty())
            .build();

    private static final Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(new GsonJava8TypeAdapterFactory())
            .registerTypeAdapterFactory(SubstitutionTypeAdapterFactory.create(autoValueClasses::get))
            .registerTypeAdapterFactory(DefaultsTypeAdapterFactory.create(autoValueClasses::get, type -> {
                if ( type instanceof Class ) {
                    return defaultValues.get(type);
                }
                if ( type instanceof ParameterizedType ) {
                    return defaultValues.get(((ParameterizedType) type).getRawType());
                }
                return null;
            }))
            .create();

    @SuppressWarnings("unused")
    private static Stream<Optional<String>> test() {
        return Stream.of(
                Optional.of("ok"),
                Optional.empty()
        );
    }

    @ParameterizedTest
    @MethodSource
    public void test(final Optional<String> optional) {
        final AvoTestClass before = AvoTestClass.create(optional);
        final String json = gson.toJson(before, AvoTestClass.class);
        final AvoTestClass after = gson.fromJson(json, AvoTestClass.class);
        Assert.assertEquals(before, after);
    }

}

这个解决方案在很大程度上是基于反射的,但是如果生成器不能完成这项工作,它只是一个解决方法(同样,不确定是否可以对它们进行配置,以便不存在这样的问题)。

相关问题