1

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

public interface Species {
    String name();
}

以及一个使用 TypeAdapterHuman实现的类。@AutoValue

@AutoValue
public abstract class Human implements Parcelable, Species {
    public static Human create(String humanVariable) {
        return new AutoValue_Human(humanVariable);
    }

    public static Human create(String name, String humanVariable) {
        return new AutoValue_Human(name, humanVariable);
    }

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

    @SerializedName("name")
    public abstract String name();

    @Nullable
    @SerializedName("human_variable")
    public abstract String humanVariable();

    @Override
    public String name() {
       return name();
    }
}

当我从我们的 API 中提取数据时,我收到了这个错误。

无法为接口调用无参数构造函数.....物种。向 Gson 注册此类型的 InstanceCreator 可能会解决此问题。

我一直在想办法解决这个问题,但运气不佳。

我找到了一些类似Serialize And Deserialize Interfaces的资源,但它们不使用 @AutoValue 或 auto-value-gson,所以不知道如何将所有内容放在一起。

任何帮助将不胜感激!

4

1 回答 1

2

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创建 -created 实例之后的实际对象类型),因为它是一个接口- 虽然不确定这是否不是错误)。

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

  • 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/john-doe
约翰·多伊

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


如果出于任何正当理由,您确实需要将 JSON 反序列化为未知Species实例并动态解析其类型,则可以创建更复杂的解决方案。它的“经典”解决方案之一是通过其特殊的 JSON 属性解析对象类型(受来自 Gson extras 的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());

输出:

类 q43267910.AutoValue_Human
13289/john-doe

于 2017-04-07T07:11:21.217 回答