我有一个数据结构,包含一些强类型字段和一些松散类型的字段。其中一些字段是可以是任何深层嵌套的集合。
示例JSON
{
"prop": "Hello", //strongly-typed
"child1": {
"anInt": -1
},
"map": { // here magic begins
"JustString": "JustValue", // we may store any Object in this map
"Item_With_Type": {
"@type": "MyMap", // some of them tell their type and we need to rely on it
"Custom": "Value"
},
"List_With_All_Child1": {
"@type": "MyMap[]", // lists define the type of all values in it in this way
"@values": [
{
"Key": "Value", // MyMap is a Map
"Child1": { // of <? extends Object>
"anInt": 2
}
},
{
"Key": "Value"
}
]
}
}
}
我想在
上映射public static class Parent {
private String prop;
private Child1 child1;
private MyMap<?> map;
}
public static class Child1 {
private int anInt;
}
public static class MyMap<T> extends HashMap<String, T> implements Map<String, T> {
}
(省略了访问者)
基本上我需要一种数据绑定器,杰克逊每次尝试解决任何字段保存环境的类型时都会询问类型,如果这个数据绑定器没有找到任何特定应用程序,杰克逊应该回退默认类型解析。
任何想法如何实现这一目标?
答案 0 :(得分:0)
在与杰克逊玩了一段时间后,我找到了以下解决方案。对我来说很好。
首先,我们将所有内容都变为多态
@JsonTypeResolver(MyTypeResolver.class)
@JsonTypeIdResolver(MyTypeIdResolver.class)
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include = JsonTypeInfo.As.PROPERTY, property = "@type")
public interface ObjectMixin {
}
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.addMixIn(Object.class, ObjectMixin.class);
我们创建自定义TypeResolver,仅处理java.lang.Object
的类型序列化/去除化。
public class MyTypeResolver extends StdTypeResolverBuilder {
@Override
public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null;
}
@Override
public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null;
}
public boolean useForType(JavaType t) {
return t.isJavaLangObject();
}
}
TypeIdResolver反过来处理ID魔术。在这个例子中,所有内容都是硬编码的,在实际代码中,它当然看起来更好。 :)
public class MyTypeIdResolver extends TypeIdResolverBase {
@Override
public String idFromValue(Object value) {
return getId(value);
}
@Override
public String idFromValueAndType(Object value, Class<?> suggestedType) {
return getId(value);
}
@Override
public JsonTypeInfo.Id getMechanism() {
return JsonTypeInfo.Id.CUSTOM;
}
private String getId(Object value) {
if (value instanceof ListWrapper.MyMapListWrapper) {
return "MyMap[]";
}
if (value instanceof ListWrapper.Child1ListWrapper) {
return "Child1[]";
}
if (value instanceof ListWrapper && !((ListWrapper) value).getValues().isEmpty()) {
return ((ListWrapper) value).getValues().get(0).getClass().getSimpleName() + "[]";
}
return value.getClass().getSimpleName();
}
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
if (id.endsWith("[]")) {
if (id.startsWith("Child1")) {
return TypeFactory.defaultInstance().constructParametricType(ListWrapper.class, Child1.class);
}
if (id.startsWith("MyMap")) {
return TypeFactory.defaultInstance().constructSpecializedType(TypeFactory.unknownType(), ListWrapper.MyMapListWrapper.class);
}
}
if (id.equals("Child1")) {
return TypeFactory.defaultInstance().constructSpecializedType(TypeFactory.unknownType(), Child1.class);
}
if (id.equals("MyMap")) {
return TypeFactory.defaultInstance().constructSpecializedType(TypeFactory.unknownType(), MyMap.class);
}
return TypeFactory.unknownType();
}
}
为了能够处理{"@type: "...", "@values": ...}
个列表,我有一个ListWrapper
类和子类。 Todo:使用自定义反序列化逻辑重新实现它。
public class ListWrapper<T> {
@JsonProperty("@values")
private List<T> values;
public static class MyMapListWrapper extends ListWrapper<MyMap> {
}
public static class Child1ListWrapper extends ListWrapper<Child1> {
}
}
可以跳过子类的创建,但随后会将类型信息添加到每个元素中。 java.lang.*
课程当然没有类型信息。
模型是:
public class Parent {
private String prop;
private Child1 child1;
private MyMap map;
}
public class Child1 {
private int anInt;
}
测试代码:
@Test
public void shouldDoTheTrick() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.addMixIn(Object.class, ObjectMixin.class);
Parent parent = new Parent("Hello", new Child1(-1), new MyMap() {{
put("JustString", "JustValue");
put("List_With_All_MyMaps", new ListWrapper.MyMapListWrapper(new ArrayList<MyMap>() {{
add(new MyMap() {{
put("Key", "Value");
put("object", new Child1(2));
}});
add(new MyMap() {{
put("Key", "Value");
}});
}}));
put("List_With_All_Child1", new ListWrapper.Child1ListWrapper(new ArrayList<Child1>() {{
add(new Child1(41));
add(new Child1(42));
}}));
}});
String valueAsString = mapper.writeValueAsString(parent);
Parent deser = mapper.readValue(valueAsString, Parent.class);
assertEquals(parent, deser);
}
JSON输出:
{
"prop" : "Hello",
"child1" : {
"anInt" : -1
},
"map" : {
"JustString" : "JustValue",
"List_With_All_MyMaps" : {
"@type" : "MyMap[]",
"@values" : [ {
"Key" : "Value",
"object" : {
"@type" : "Child1",
"anInt" : 2
}
}, {
"Key" : "Value"
} ]
},
"List_With_All_Child1" : {
"@type" : "Child1[]",
"@values" : [ {
"anInt" : 41
}, {
"anInt" : 42
} ]
}
}
}
UPD:真正的实施示例https://github.com/sdl/dxa-web-application-java/commit/7a36a9598ac2273007806285ea4d854db1434ac5