我有一个看起来像这样的接口类。
public interface Species {
String name();
}
使用TypeAdapter实现Human
的{{1}}类。
@AutoValue
当我从API下载数据时,我收到此错误。
无法为接口调用no-args构造函数.....物种。 使用Gson注册InstanceCreator可以解决此问题 问题
我一直试图弄清楚如何处理这个问题但是没有多少运气。
我发现了一些类似Serialize And Deserialize Interfaces的资源,但他们不使用@AutoValue或auto-value-gson,所以不确定如何将所有内容放在一起。
任何帮助都会非常感激!
答案 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
类型适配器工厂。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