带注释的GSON自定义序列化

jda*_*ski 6 java reflection android annotations gson

我有一个非常具体的GSON自定义序列化案例:

假设我有以下课程:

public class Student extends BaseModel{
    private int id;
    private String name;
    private Student goodFriend;
    private Student bestFriend;
}
Run Code Online (Sandbox Code Playgroud)

BaseModel只是我所有模型类的基类.

当我这么做的时候

gson.toJson(student /* Some Student instance */);
Run Code Online (Sandbox Code Playgroud)

我会举个例子:

{
 id: 1, 
 name: "Jack", 
 goodFriend: {id: 2, name: "Matt"}, 
 bestFriend: {id: 3, name "Tom"}
}
Run Code Online (Sandbox Code Playgroud)

这很好,但我需要的是:

{
 id: 1, 
 name: "Jack", 
 goodFriend: 2, // only an ID for this field
 bestFriend: {id: 3, name "Tom"} // whole object for this field
 // both fields are of the same Type, so I can't use TypeAdapterFactory for this
}
Run Code Online (Sandbox Code Playgroud)

我需要一些方法用序列化类型(id或object)标记字段,然后根据需要使用该标记进行序列化.我一般如何做到这一点,不仅仅是针对Student类,而是针对BaseModel的所有子类?

我唯一的想法是使用自定义注释:将我想要序列化的字段描述为只有一个注释的ID,以及我希望序列化为具有另一个注释的对象的字段,但我找不到一种方法来检索注释TypeAdapter的写方法.

任何想法如何处理这个?

jda*_*ski 7

我自己找到了答案.事实证明,在GSON中已经存在这种情况的注释.它叫做@JsonAdapter.

首先,我必须创建一个TypeAdapterFactory:

public class BaseModelForeignKeyTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (!BaseModel.class.isAssignableFrom(type.getRawType())) {
            return null;
        }

        TypeAdapter defaultAdapter = gson.getAdapter(type);

        //noinspection unchecked
        return (TypeAdapter<T>) new Adapter(defaultAdapter);
    }

    private static class Adapter<T extends BaseModel> extends TypeAdapter<T> {

        private final TypeAdapter<T> defaultAdapter;

        Adapter(TypeAdapter<T> defaultAdapter) {
            this.defaultAdapter = defaultAdapter;
        }

        @Override
        public void write(JsonWriter out, T value) throws IOException {
            out.value(value.getId());
        }

        @Override
        public T read(JsonReader in) throws IOException {
            return defaultAdapter.read(in);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

create()方法中,我检索Gson将用于此字段的默认适配器,并将其传递给适配器,以便在反序列化字段时使用.这样,此适配器仅用于序列化,而反序列化则委托给默认适配器.

现在我只需要注释我的Student类中的字段,我想用这个TypeAdapterFactory将其序列化为ID,如下所示:

public class Student extends BaseModel{
    private int id;
    private String name;

    @JsonAdapter(BaseModelForeignKeyTypeAdapterFactory.class)
    private Student goodFriend;

    private Student bestFriend;
}
Run Code Online (Sandbox Code Playgroud)

这就是全部,现在gson.toJson(student)将输出:

{
 id: 1, 
 name: "Jack", 
 goodFriend: 2, // using "ForeignKey" TypeAdapter
 bestFriend: {id: 3, name "Tom"} // using default TypeAdapter
}
Run Code Online (Sandbox Code Playgroud)

我希望这可以帮助别人!