具有接口错误的Auto-value-gson,注册InstanceCreator?

时间:2017-04-07 00:42:00

标签: android interface gson auto-value

我有一个看起来像这样的接口类。

public interface Species {
    String name();
}

使用TypeAdapter实现Human的{​​{1}}类。

@AutoValue

当我从API下载数据时,我收到此错误。

  

无法为接口调用no-args构造函数.....物种。   使用Gson注册InstanceCreator可以解决此问题   问题

我一直试图弄清楚如何处理这个问题但是没有多少运气。

我发现了一些类似Serialize And Deserialize Interfaces的资源,但他们不使用@AutoValue或auto-value-gson,所以不确定如何将所有内容放在一起。

任何帮助都会非常感激!

1 个答案:

答案 0 :(得分:1)

InstanceCreator在Gson中并不经常使用,Gson建议在这种情况下造成一些混淆,通常可以用类型适配器(工厂)替换。 InstanceCreator界面只能创建一个默认实例,该实例不会与您尝试反序列化的JSON合并。例如:

{
    "name": "13289/john-doe",
    "human_variable": "John Doe"
}
public static Human create() {
    return new AutoValue_Human("anonymous", null);
}
private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Species.class, (InstanceCreator<Species>) type -> Human.create())
        .create();
final Species species = gson.fromJson(jsonReader, Species.class);
System.out.println(species.name());

输出:

  

匿名

在这种情况下,您只能将Species接口与默认的Human实例绑定。根据这一点,species.name()只返回anonymous而不管JSON(Gson内部ReflectiveTypeAdapterFactory.Adapter只是跳过所有JSON字段(实际上,它首先针对给定字段声明类型收集所有字段,而不是创建InstanceCreator创建的实例后的实际对象类型,因为它是一个界面 - 不确定它是不是一个bug)。

您真正需要的是以下步骤:

  • 如果您尚未使用,请使用com.ryanharter.auto.value:auto-value-gson:...
  • 注册使用Human扩展名创建的auto-value-gson类型适配器工厂。
  • 将您的JSON反序列化为具体的Human实例,而不是Species实例。

例如:

@GsonTypeAdapterFactory
abstract class HumanAdapterFactory
        implements TypeAdapterFactory {

    public static TypeAdapterFactory create() {
        return new AutoValueGson_HumanAdapterFactory();
    }

}
private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(HumanAdapterFactory.create())
        .create();
final Human human = gson.fromJson(jsonReader, Human.class);
System.out.println(human.name());
System.out.println(human.humanVariable());

输出:

  

13289 /约翰-DOE
  John Doe

这是我最推荐的解决方案。

如果出于任何正当理由,您确实需要将JSON反序列化为未知Species实例并动态解析其类型,则可以创建更复杂的解决方案。其中一个经典&#34;解决方案是通过其特殊的JSON属性解析对象类型(受到来自Gson附加组件的RuntimeTypeAdapterFactory的启发,但未作为工件发布):

{
    "type": "human",
    "name": "13289/john-doe",
    "human_variable": "John Doe"
}
private static final Gson gson = new GsonBuilder()
        // We'll ask Gson for it ourselves
        .registerTypeAdapterFactory(HumanAdapterFactory.create())
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                // Check whether we can support the given type
                if ( Species.class.isAssignableFrom(typeToken.getRawType()) ) {
                    final TypeAdapterFactory currentTypeAdapterFactory = this;
                    // And get the "original" type adapter
                    @SuppressWarnings("unchecked")
                    final TypeAdapter<Species> delegateTypeAdapter = (TypeAdapter<Species>) gson.getDelegateAdapter(this, typeToken);
                    final TypeAdapter<Species> speciesTypeAdapter = new TypeAdapter<Species>() {
                        // Type tokens can be static since they are immutabe
                        private /*static*/ final TypeToken<Human> humanTypeToken = TypeToken.get(Human.class);
                        // JsonParser seems to be immutable as well
                        private /*static*/ final JsonParser jsonParser = new JsonParser();

                        @Override
                        public void write(final JsonWriter out, final Species value)
                                throws IOException {
                            delegateTypeAdapter.write(out, value);
                        }

                        @Override
                        public Species read(final JsonReader in)
                                throws IOException {
                            // Caching the current value to a JSON tree
                            final JsonElement jsonElement = jsonParser.parse(in);
                            final String type = jsonElement.getAsJsonObject()
                                    .getAsJsonPrimitive("type")
                                    .getAsString();
                            final TypeAdapter<? extends Species> typeAdapter;
                            // Now trying to resolve an appropriate type adapter
                            switch ( type ) {
                            case "human":
                                typeAdapter = gson.getDelegateAdapter(currentTypeAdapterFactory, humanTypeToken);
                                break;
                            default:
                                throw new MalformedJsonException("Unknown type: " + type);
                            }
                            // At this point the JsonReader is moved formed due to the previous read, but we have the JSON tree
                            return typeAdapter.fromJsonTree(jsonElement);
                        }
                    }.nullSafe();
                    @SuppressWarnings("unchecked")
                    final TypeAdapter<T> castSpeciesTypeAdapter = (TypeAdapter<T>) speciesTypeAdapter;
                    return castSpeciesTypeAdapter;
                }
                return null;
            }
        })
        .create();
final Species species = gson.fromJson(jsonReader, Species.class);
System.out.println(species.getClass());
System.out.println(species.name());

输出:

  

class q43267910.AutoValue_Human
  13289 / john-doe