Java 动态类型的Jackson多态反序列化

Java 动态类型的Jackson多态反序列化,java,json,jackson,Java,Json,Jackson,我有一个包含一些强类型字段和一些松散类型字段的数据结构。其中一些字段是可以是任何深度嵌套的集合 示例JSON { "prop": "Hello", //strongly-typed "child1": { "anInt": -1 }, "map": { // here magic begins "JustString": "JustValue", // we may store any Obje

我有一个包含一些强类型字段和一些松散类型字段的数据结构。其中一些字段是可以是任何深度嵌套的集合

示例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"
        }
      ]
    }
  }
}
{
“prop”:“Hello”,//强类型
“儿童1”:{
“anInt”:-1
},
“地图”:{//魔法开始了
“JustString”:“JustValue”,//我们可以在此映射中存储任何对象
“带有类型的项目”:{
“@type”:“MyMap”//有些人告诉我们他们的类型,我们需要依赖它
“自定义”:“值”
},
“列出所有子女的子女1”:{
“@type”:“MyMap[]”,//列表以这种方式定义其中所有值的类型
“@values”:[
{
“键”:“值”//MyMap是一个映射
“Child1”:{//map;
}
公共静态类Child1{
私人内部信息;
}
公共静态类MyMap扩展HashMap实现Map{
}
(省略访问器)

基本上,我需要一种数据绑定器,Jackson每次尝试为任何字段保存上下文解析类型时都会询问该类型,如果该数据绑定器没有找到任何特定于应用程序的内容,Jackson应该返回默认类型解析


有什么办法可以做到这一点吗?

和杰克逊一起玩了一段时间后,我想到了以下解决方案。对我来说效果很好

首先,我们让一切都是多态的

@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);
我们创建的自定义类型解析器只处理
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();
    }
}
可以跳过子类的创建,但是类型信息会添加到每个元素中。
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);
}

UPD:真正的实现示例

请不要在堆栈溢出上的问题中发布代码链接。试试
ObjectMapper
它可能适合您。
public class ListWrapper<T> {    
    @JsonProperty("@values")
    private List<T> values;

    public static class MyMapListWrapper extends ListWrapper<MyMap> {
    }

    public static class Child1ListWrapper extends ListWrapper<Child1> {
    }
}
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);
}
{
  "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
      } ]
    }
  }
}