Java 具有内部抽象类的容器类的Gson反序列化

Java 具有内部抽象类的容器类的Gson反序列化,java,gson,Java,Gson,我发现使用GSON反序列化这个类有点困难。 从这里的资源抽象类开始: public abstract class Resource() { String id; Integer quantity; } public class Wood extends Resource { } public class Stone extends Resource{ } 然后我有一个容器类: public class ResourceSet { Map<String, Resource> re

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

public abstract class Resource() {
String id;
Integer quantity;
}

public class Wood extends Resource {
}

public class Stone extends Resource{
}
然后我有一个容器类:

public class ResourceSet {
Map<String, Resource> resourcesMap;
}

一个选项是为
资源
类型创建自定义反序列化器。在其中,在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;
    }

}

这是一个很好的响应,但问题是我想为资源集而不是资源编写json反序列化程序。假设我想反序列化这个json字符串:
{“resourcesMap”:{“Wood”:{“quantity”:4},“Stone”:{“quantity”:2}}}
在这种情况下,您可以“向上一级”并反序列化
映射本身。我在我的答案中添加了一个更新,以演示我所说的内容。
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);
    }

}