Java 使用Jackson将JSON反序列化为异构元素列表

Java 使用Jackson将JSON反序列化为异构元素列表,java,json,data-structures,jackson,Java,Json,Data Structures,Jackson,在我的JSON中,我有一个包含以下内容的元素: { ... "locations": [ [ { "location_type": "permanent", "position": "at", "accuracy": "exact" }, "and", {

在我的JSON中,我有一个包含以下内容的元素:

{
    ...
    "locations": [
        [
            {
                "location_type": "permanent",
                "position": "at",
                "accuracy": "exact"
            },
            "and",
            {
                "location_type": "permanent",
                "position": "in",
                "accuracy": "exact"
            }
        ],
        "or",
        {
            "location_type": "temporary",
            "position": "at",
            "accuracy": "exact"
        }
    ],
    ...
}
如图所示,
位置的元素可以是:

  • 地点
  • 逻辑运算符
  • 位置列表(允许复杂位置)
我得到“无法反序列化
com.example.processor.transformation.json.Location的实例”
out-of-START\u数组令牌”

如何使用Jackson将其转换为数据结构?

到目前为止,我尝试的是:

  • 提供一个
    位置(String logicalOperator)
    构造函数有助于简化列表。(我基本上将运算符转换为特殊值
    位置
  • 添加
    位置(列出子位置)
    位置(位置[]子位置)
    构造函数对这种情况没有帮助

  • 注意:我无法控制JSON格式,因此无法以更为Jackson友好的方式对其进行编码。

    为此,您需要一个自定义反序列化程序。你不能只添加一个构造函数

    下面是一个包含类
    Foo
    的自包含示例,它可以由自己的属性
    “Foo”:“someString”
    ,或者由某个逻辑运算符
    “和”
    或“
    等表示为
    字符串
    文本,用于表示
    Foo
    实例,其
    Foo
    属性将是该文本的值

    这可能完全适合您的情况,也可能不适合您的情况,但您可以进行调整

    换言之:

    • {“foo”:“a”}
      -->
      新foo(“a”)
    • “或”
      -->
      新食品(“或”)
    示例

    // given...
    
    @JsonDeserialize(using=MyDeserializer.class)
    class Foo {
        String foo;
        public void setFoo(String s) {
            foo = s;
        }
        public String getFoo() {
            return foo;
        }
        public Foo(String s) {
            setFoo(s);
        }
    }
    
    //和自定义反序列化程序

    class MyDeserializer extends JsonDeserializer<Foo> {
    
        @Override
        public Foo deserialize(JsonParser jp, DeserializationContext ct)
                throws IOException, JsonProcessingException {
            ObjectCodec oc = jp.getCodec();
            JsonNode node = oc.readTree(jp);
            // this JSON object has a "foo" property, de-serialize 
            // injecting its value in Foo's constructor
            if (node.has("foo")) {
                return new Foo(node.get("foo").asText());
            }
            // other case, assuming literal (e.g. "and", "or", etc.)
            // inject actual node as String value into Foo's constructor
            else {
                return new Foo(node.asText());
            }
        }
    
    }
    
    // here's a quick example
    
    String json = "[{\"foo\": \"a\"}, \"or\", {\"foo\": \"b\"}]";
    ObjectMapper om = new ObjectMapper();
    List<Foo> list = om.readValue(json, new TypeReference<List<Foo>>(){});
    list.forEach(f -> System.out.println(f.foo));
    
    为清晰起见,请注意

    这是一个非常简单的例子。 在您的例子中,您可能需要一个多态的
    Location
    pojo集合,它与
    LogicalOperator
    pojo(或类似的东西)混合在一起,共享一个公共的标记接口。
    然后,您可以根据JSON节点是以内容为特征(即位置)还是JSON节点是其内容(例如逻辑运算符)来决定反序列化哪个对象

    为此,您需要一个自定义反序列化程序。您不能只添加构造函数。@Saifhmad请查看重复状态。这是一个不同的问题,有不同的解决方案。虽然这不是最终的解决方案,但我认为你为我指明了正确的方向。我试着在不同的地方应用
    @JsonDeserialize
    ,而
    Location
    是一个接口,但这似乎并没有触发自定义反序列化程序。@AdSR“在不同的地方”是什么意思?您只需要在类/接口声明之前进行注释。如果您的位置和逻辑运算符有一个公共接口,那么可以在该接口声明之前进行注释。没关系,我尝试使用错误的Jackson包,但它与解析器不匹配(
    org.codehaus…
    vs.
    com.fasterxml…
    )。最后,您的解决方案和我的解决方案之间的唯一区别是,我通过
    node.isArray()
    etc.@adsraha对节点类型进行分派。是的,
    org.codehaus
    现在已经很旧了,这让我想起了我自己的答案:D您在自己的反序列化程序中有很多选择,最终归结为您发现哪种方法更容易处理您的全部负载。
    a
    or
    b