带有内部抽象类的容器类的Gson deserializaton

时间:2017-06-19 22:05:30

标签: java gson

我发现使用GSON反序列化这个类有点困难。 从这里的Resource ABSTRACT类开始:

{
"resourcesMap":{
                "Wood":{"quantity":4},
                "Stone":{"quantity":2}
               }
}

然后我有一个容器类:

{{1}}

在地图中,键是资源的ID,而值是资源本身。 我不知道如何正确地反序列化这个类,让gson知道当他解析字符串“Wood”时,资源就是Wood的一个实例。否则gson使用作为抽象类的资源类的基础构造函数,因此它给了我一个例外。

我想要反序列化的json字符串的示例可能是:

{{1}}

1 个答案:

答案 0 :(得分:1)

一个选项是为Resource类型创建自定义反序列化程序。在其中,使用JSON消息中的提示来确定Resource应该是什么类型。

我不确定您的JSON消息是什么样的,但解决方案看起来像这样:

public class ResourceDeserializer implements JsonDeserializer<Resource> {

    @Override
    public Resource deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        final JsonObject root = json.getAsJsonObject();

        final Resource resource;

        if("wood".equalsIgnoreCase(root.get("type"))) {
            resource = new WoodResource();
        } else {
            resource = new StoneResource();
        }

        return resource;
    }

}

<强>已更新

abstract class Resource {
    protected String id;
    protected Integer quantity;
}

class Wood extends Resource {}

class Stone extends Resource {}

class ResourceMap {
    protected Map<String,Resource> resources;

    ResourceMap() {
        this.resources = new HashMap<>();
    }
}

class ResourceMapDeserializer implements JsonDeserializer<ResourceMap> {

    @Override
    public ResourceMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        final JsonObject root = json.getAsJsonObject();

        final ResourceMap instance = new ResourceMap();
        instance.resources.put("Wood", parse(root, "Wood"));
        instance.resources.put("Stone", parse(root, "Stone"));

        return instance;
    }

    private Resource parse(JsonObject root, String fieldName) {
        final JsonElement field = root.get(fieldName);

        if(field != null) {
            final Resource resource;

            if("Wood".equalsIgnoreCase(fieldName)) {
                resource = new Wood();
            } else {
                resource = new Stone();
            }

            resource.quantity = field.getAsJsonObject().get("quantity").getAsInt();

            return resource;

        } else {
            return null;
        }
    }

}

简单验证......

@RunWith(MockitoJUnitRunner.class)
public class ResourceDeserializerTest {

    @Mock private JsonDeserializationContext mockContext;

    private Gson gson;

    @Before
    public void setUp() {
        gson = new GsonBuilder()
                .registerTypeAdapter(ResourceMap.class, new ResourceMapDeserializer())
                .setPrettyPrinting()
                .create();
    }

    @Test
    public void deserializes_resource_map() {
        final JsonObject woodJson = new JsonObject();
        woodJson.addProperty("quantity", 4);

        final JsonObject stoneJson = new JsonObject();
        stoneJson.addProperty("quantity", 2);

        final JsonObject mapJson = new JsonObject();
        mapJson.add("Wood", woodJson);
        mapJson.add("Stone", stoneJson);

        final ResourceMap deserialized = gson.fromJson(mapJson, ResourceMap.class);

        assertThat(deserialized.resources.get("Wood").getClass()).isEqualTo(Wood.class);

        assertThat(deserialized.resources.get("Stone").getClass()).isEqualTo(Stone.class);
    }

}