抽象类的自定义 gson 序列化

rek*_*ire 3 java serialization gson

也许我跑错了方向,但我有一个我想阅读的元素列表。

我有一个抽象基类,我们称之为Person

public abstract class Person {
    public int id;
    public String name;
}
Run Code Online (Sandbox Code Playgroud)

现在我有两种可能的实现:

public class Hunter implements Person {
    public int skill;
    // and some more stuff
}

public class Zombie implements Person {
    public int uglyness;
    // and some more stuff
}
Run Code Online (Sandbox Code Playgroud)

现在我有这个示例 JSON:

[
  {"id":1, "type":"zombie", "name":"Ugly Tom", "uglyness":42},
  {"id":2, "type":"hunter", "name":"Shoot in leg Joe", "skill":0}
]
Run Code Online (Sandbox Code Playgroud)

我怎样才能读这个 JSON 为List<Person>

我玩了一段时间TypeAdapterFactory并尝试使用一个名为的类,CustomizedTypeAdapterFactory因为我的真实结构比上面有趣的例子要复杂一些。

我最后说我想通过这个调用委托序列化:

return gson.getDelegateAdapter(this, resultType);
Run Code Online (Sandbox Code Playgroud)

但是,我不知道如何在运行时创建TypeToken<T>此调用所需的内容。有任何想法吗?

Ale*_* C. 5

如何将此 JSON 作为列表读取?

一种可能性是创建一个像工厂一样的自定义解串器。

第一步是定义这个解串器

class PersonJsonDeserializer implements JsonDeserializer<Person> {
    @Override
    public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        String type = json.getAsJsonObject().get("type").getAsString();
        switch(type) {
            case "zombie":
                return context.deserialize(json, Zombie.class);
            case "hunter":
                return context.deserialize(json, Hunter.class);
            default:
                throw new IllegalArgumentException("Neither zombie or hunter");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它获取与键“类型”关联的值并选择正确的类型来反序列化您当前正在读取的对象。

然后,您需要在解析器中插入这个反序列化器。

public class GsonTest {
    public static void main(String[] args) {
        String json = "[\n" +
                "  {\"id\":1, \"type\":\"zombie\", \"name\":\"Ugly Tom\", \"uglyness\":42},\n" +
                "  {\"id\":2, \"type\":\"hunter\", \"name\":\"Shoot in leg Joe\", \"skill\":0}\n" +
                "]";

        Gson gson = new GsonBuilder().registerTypeAdapter(Person.class, new PersonJsonDeserializer()).create();

        Type type = new TypeToken<List<Person>>(){}.getType();

        List<Person> list = gson.fromJson(json, type);

        for(Person p : list) {
            System.out.println(p);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用你的例子运行它,我得到:

Zombie{id=1; name=Ugly Tom; uglyness=42}
Hunter{id=2; name=Shoot in leg Joe; skill=0}
Run Code Online (Sandbox Code Playgroud)

如果类型的值已经对应于类名,您可能还想使用Class.forName

class PersonJsonDeserializer implements JsonDeserializer<Person> {
    @Override
    public Person deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        String className = json.getAsJsonObject().get("type").getAsString();
        className = Character.toUpperCase(className.charAt(0)) + className.substring(1);
        try {
            return context.deserialize(json, Class.forName(className));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)