带接口错误的Auto-value-gson,注册一个InstanceCreator? [英] Auto-value-gson with an interface error, register an InstanceCreator?
问题描述
我有一个看起来像这样的接口类。
I have an interface class that looks like this.
public interface Species {
String name();
}
以及人
使用TypeAdapter实现 @AutoValue
的类。
And a Human
class that implements @AutoValue
with a TypeAdapter.
@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下载数据时,我得到了这个错误。
When I pull down data from our API I get this error.
无法为接口调用no-args构造函数.....物种。
注册一个InstanceCreator与Gson这种类型可以解决这个
问题。
Unable to invoke no-args constructor for interface ..... Species. Register an InstanceCreator with Gson for this type may fix this problem.
我一直在尝试弄清楚如何处理这个问题,但运气不好。
I've been trying to figure out how to handle this but haven't had much luck.
我发现了一些这样的资源序列化和反序列化接口但它们不使用@AutoValue或auto-value-gson所以不确定如何放置所有内容一起。
I found some resources like this Serialize And Deserialize Interfaces but they don't use @AutoValue or auto-value-gson so not sure how to put everything together.
任何帮助都会非常感激!
Any help would be much appreciate!
推荐答案
InstanceCreator
并不常用于Gson,Gson建议在这种情况下造成一些混淆,通常可以用类型适配器(工厂)替换。 InstanceCreator
界面只能创建一个默认实例,该实例不会与您尝试反序列化的JSON合并。例如:
InstanceCreator
is not often used in Gson, suggested by Gson in such cases making some confusion, and usually can be replaced with type adapters (factories). The InstanceCreator
interface can only create a default instance that won't be merged with the JSON you're trying to deserialize. For example:
{
"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());
输出:
anonymous
anonymous
在这种情况下,你绑定 Species
界面仅使用默认的 Human
实例。据此, species.name()
只返回 anonymous
,无论JSON如何(Gson内部 ReflectiveTypeAdapterFactory.Adapter
只是跳过所有JSON字段(实际上,它首先针对给定字段声明类型收集所有字段,而不是 InstanceCreator $ c $之后的实际对象类型c> -created instance is created)因为它是一个接口 - 不确定它是不是一个bug。)
In this case you'd bind the Species
interface with the default Human
instance only. According to that, species.name()
would return anonymous
only regardless the JSON (Gson internal ReflectiveTypeAdapterFactory.Adapter
just skips all JSON fields (actually, it collects all fields first against the given field declared type, not an actual object type after the InstanceCreator
-created instance is created) because it's an interface -- not sure if it's not a bug though).
你真正需要的是以下步骤:
What you really need here is the following steps:
- 使用
com.ryanharter.auto.value:auto-value-gson:...
如果你还没有使用。 - 注册使用
auto创建的
extension。Human
类型适配器工厂-value-gson - 将JSON反序列化为具体的
Human
实例,而不是物种
实例。
- Use
com.ryanharter.auto.value:auto-value-gson:...
if you're not using yet. - Register the
Human
type adapter factory created using theauto-value-gson
extension. - Deserialize your JSON as a concrete
Human
instance rather than aSpecies
instance.
例如:
@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
John Doe
13289/john-doe
John Doe
这是我最推荐的解决方案。
This is the solution I would recommend the most.
如果出于任何正当理由,您确实需要将JSON反序列化为未知物种
实例并动态解析其类型,您可以创建更复杂的解决方案。其经典解决方案之一是通过其特殊的JSON属性解析对象类型(受 RuntimeTypeAdapterFactory ,但未作为工件发布):
If, for any justified reason, you really need to deserialize a JSON as an unknown Species
instance and resolve its type dynamically, you could create a more complex solution. One of its "classic" solutions is resolving an object type by its special JSON property (inspired by RuntimeTypeAdapterFactory coming from the Gson extras, not published as an artifact though):
{
"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
class q43267910.AutoValue_Human
13289/john-doe
这篇关于带接口错误的Auto-value-gson,注册一个InstanceCreator?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!