使用jackson JSON库,我能够生成带有类型字段信息的JSON并将其读回。但是,我的JSON对象的某些客户端可能无法访问特定的类。我希望他们将JSON属性序列化为Map。这似乎无法正常工作,如下面的代码所示。如何做到这一点?
static class Foo {
@JsonProperty
private String x;
@JsonProperty
@JsonTypeInfo(use= JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
private Object o;
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public Object getO() {
return o;
}
public void setO(Object o) {
this.o = o;
}
}
@Test
public void testJacksonTypedSerialization() throws Exception {
Foo foo = new Foo();
foo.setX("hello world!");
Foo foo2 = new Foo();
foo2.setX("inner");
foo.setO(foo2);
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(foo);
System.out.println("foo is written as " + str);
assertTrue(str.contains("Foo"));
assertTrue(str.contains("@class"));
Foo read = mapper.readValue(str, Foo.class);
assertEquals(read.getX(), foo.getX());
assertNotNull(read.getO());
assertTrue(read.getO() instanceof Foo);
String str2 = str.replace("Foo", "NoSuchType");
Foo read2 = mapper.readValue(str2, Foo.class);
assertEquals(read2.getX(), foo.getX());
assertNotNull(read2.getO());
// in this case, I want Jackson to read 'o' as Java Map/List
assertTrue(read.getO() instanceof Map);
// !!! encountered error
//com.fasterxml.jackson.databind.JsonMappingException: Invalid type id 'com.mycorp.NoSuchType' (for id type 'Id.class'): no such class found
}
答案 0 :(得分:0)
好吧,我认为随着你付出更多的努力,你可以逐渐接近解决方案。理论上,你应该能够编写一个自定义类型解析器,如果该类不存在则返回unknown(默认Id.CLASS
解析器永远不会这样做)。不过,这很快变成了比我尝试时更深入的东西。
更简单的解决方案是配置标准名称解析器(可以返回“type unknown”)并指定defaultImpl。类似的东西:
@JsonProperty
@JsonTypeInfo(defaultImpl=JsonNode.class, use= JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({ @JsonSubTypes.Type(name="foo-type", value=Foo.class) })
public Object o;
我发现有一件事是你将defaultImpl指定为Map,然后它不会像地图一样序列化,而是尝试将Map类视为bean(因此找不到属性)。您可以保留JsonNode或使用对象映射器将其正确转换为Map。
我真的不确定这种交换是否可行 - 一旦你在类型层次结构中进行反序列化,你可能已经承诺反序列化为bean?在这种情况下,您将进入自定义反序列化器领域。
我稍稍调整了测试,以便在'o'上传递这些注释:
@Test
public void testJacksonTypedSerialization() throws Exception {
Foo foo = new Foo();
foo.setX("hello world!");
Foo foo2 = new Foo();
foo2.setX("inner");
foo.setO(foo2);
String str = mapper.writeValueAsString(foo);
System.out.println("foo is written as " + str);
assertThat(str, both(containsString("foo-type")).and(containsString("@type")));
Foo read = mapper.readValue(str, Foo.class);
assertThat(read.getX(), equalTo(foo.getX()));
assertThat(read.getO(), instanceOf(Foo.class));
String str2 = str.replace("foo-type", "NoSuchType");
Foo read2 = mapper.readValue(str2, Foo.class);
assertThat(read2.getX(), equalTo(foo.getX()));
assertThat(read2.getO(), instanceOf(ObjectNode.class));
}
答案 1 :(得分:0)
现在,我将嵌套对象存储为显式Map
// pseudo-codes
// writing out
Foo foo = new Foo();
Map objAsMap = mapper.read(mapper.writeAsString(inner), Map.class);
objAsMap.put("@advisoryJavaClass", inner.getClass().gerName());
foo.setObjMap(objAsMap);
String json = mapper.writeAsString(foo);
// reading back
Foo foo2 = mapper.read(json, Foo.class);
Object innerAttr = null;
if (foo2.getObjMap() != null) {
try {
Class cls = Class.forName(foo2.getObjectMap().get("@advistoryJavaClass");
innerAttr = mapper.read(mapper.writeAsString(foo2.getObjMap()), cls);
} catch(ex) { } // swallow all class look up error
}
注意:将内部属性对象存储为Map而不是字符串的优点是生成的JSON更漂亮。长期解决方案是等待杰克逊解决这个问题。