带接口错误的Auto-value-gson,注册一个InstanceCreator? [英] Auto-value-gson with an interface error, register an InstanceCreator?

查看:217
本文介绍了带接口错误的Auto-value-gson,注册一个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 -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创建的 Human 类型适配器工厂-value-gson extension。

  • 将JSON反序列化为具体的 Human 实例,而不是物种实例。

  • Use com.ryanharter.auto.value:auto-value-gson:... if you're not using yet.
  • Register the Human type adapter factory created using the auto-value-gson extension.
  • Deserialize your JSON as a concrete Human instance rather than a Species 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆