2

我有这门课

public static class SomeClass {
    public SomeClass(String field) {
        this.field = field;
    }

    private final String field;

    public String getField() {
        return field;
    }
}

我也有这个测试(已编辑

@Test
public void testStringifyMapOfObjects() {
    Map<String, SomeClass> original = Maps.newTreeMap();
    original.put("first", new SomeClass("a"));
    original.put("second", new SomeClass("b"));
    String encoded = JsonUtil.toJson(original);
    Map<String, SomeClass> actual = JsonUtil.fromJson(encoded, Map.class);
    Assert.assertEquals("{'first':{'field':'a'},'second':{'field':'b'}}", encoded.replaceAll("\\s", "").replaceAll("\"", "'"));
    Assert.assertEquals(original.get("first"), actual.get("first"));
}

测试失败

junit.framework.AssertionFailedError: expected:<eu.ec.dgempl.eessi.facade.transport.test.TestToolTest$SomeClass@6e3ed98c> but was:<{field=a}>
    at junit.framework.Assert.fail(Assert.java:47)
    at junit.framework.Assert.failNotEquals(Assert.java:277)
    at junit.framework.Assert.assertEquals(Assert.java:64)
    at junit.framework.Assert.assertEquals(Assert.java:71)
    at eu.ec.dgempl.eessi.facade.transport.test.TestToolTest.testStringifyMapOfObjects(TestToolTest.java:90)

我可以让 json 正确地将对象序列化为地图的值还是应该使用其他东西?

已编辑

public class JsonUtil {
    private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(JsonUtil.class);

    public static <T> String toJson(T data) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(Feature.INDENT_OUTPUT, true);
        try {
            return mapper.writeValueAsString(data);
        } catch (IOException e) {
            LOG.warn("can't format a json object from [" + data + "]", e);
            return null;
        }
        //
        // return Json.stringify(Json.toJson(data));
    }

    public static <T> T fromJson(String description, Class<T> theClass) {
        try {
            JsonNode parse = new ObjectMapper().readValue(description, JsonNode.class);
            T fromJson = new ObjectMapper().treeToValue(parse, theClass);
            return fromJson;
        } catch (JsonParseException e) {
            // throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
            LOG.warn("can't parse a json object from [" + description + "]", e);
            return null;
        } catch (JsonMappingException e) {
            // throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
            LOG.warn("can't parse a json object from [" + description + "]", e);
            return null;
        } catch (IOException e) {
            // throw new RuntimeException("can't parse a json object of type " + theClass.getName() + " from [" + description + "]", e);
            LOG.warn("can't parse a json object from [" + description + "]", e);
            return null;
        }
    }
}
4

2 回答 2

3

您遇到了与 Java 泛型相关的问题。总而言之,当将数据反序列化为不可具体化的类型(也就是在运行时无法获得实际类型信息的类型)时,您需要使用超类型 token。通过阅读这些 SO 帖子,您可以获得有关什么是超类型令牌(以及为什么需要使用超类型令牌)的更多详细信息:

并且还来自杰克逊文档:

基本问题是,当您使用典型的泛型对象时,该对象的实际类型参数在运行时不可用。因此杰克逊不知道将您的数据实例化和反序列化到哪个实际类中

解决该问题的最简单方法是向您的 JSON 实用程序类添加一个重载,它接受类型引用(而不是 a Class<T>)。例如:

public static <T> T fromJson(String json, TypeReference<T> typeRef) {
     if(json == null || typeRef == null) return null;

     return new ObjectMapper().readValue(json, typeRef);
}

要这样使用:

Map<String, SomeClass> actual = JsonUtil.fromJson(
    encoded,
    new TypeReference<Map<String, SomeClass>>(){});
于 2013-05-21T19:07:50.593 回答
0

我发现最简单的解决方案是创建一个包含地图的“容器”类。这可能是因为容器有足够的地图类型细节,而不是直接使用地图的情况。

public static class SomeClass {
    private final String field;

    private SomeClass() {
        this("wrong");
    }
    public SomeClass(String field) {
        this.field = field;
    }

    public String getField() {
        return field;
    }
    @Override
    public String toString() {
        return "SomeClass[" + field + "]";
    }
}

public static class SomeClassContainer {
    private final Map<String, SomeClass> all = Maps.newTreeMap();

    public Map<String, SomeClass> getAll() {
        return all;
    }
}

在此之后......更新的测试是

@Test
public void testStringifyMapOfObjects() {
    SomeClassContainer original = new SomeClassContainer();
    original.getAll().put("first", new SomeClass("a"));
    original.getAll().put("second", new SomeClass("b"));
    String encoded = JsonUtil.toJson(original);
    System.out.println(encoded);
    SomeClassContainer actual = JsonUtil.fromJson(encoded, SomeClassContainer.class);
    System.out.println(ObjectUtils.toString(actual));
    Assert.assertEquals("{'all':{'first':{'field':'a'},'second':{'field':'b'}}}", encoded.replaceAll("\\s", "").replaceAll("[\"]", "'"));
    Assert.assertEquals("class eu.ec.dgempl.eessi.facade.transport.test.TestToolTest$SomeClass", actual.getAll().get("first").getClass().toString());
    Assert.assertEquals(original.getAll().get("first").toString(), actual.getAll().get("first").toString());
    Assert.assertEquals(original.getAll().get("second").toString(), actual.getAll().get("second").toString());
}
于 2013-05-21T14:48:09.500 回答