将杰克逊的JSON解析为异构元素列表

时间:2018-04-16 09:10:17

标签: java json data-structures jackson

在我的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"
        }
    ],
    ...
}

如图所示,locations的元素可以是:

  • 一个位置
  • 逻辑运算符
  • 地点列表(允许复杂的位置)

我得到"无法从START_ARRAY令牌"中反序列化com.example.processor.transformation.json.Location的实例。

如何使用Jackson将其用于数据结构?

到目前为止我尝试了什么:

  1. 提供Location(String logicalOperator)构造函数有助于查找平面列表。 (我基本上将运算符转换为特殊值Location。)
  2. 添加Location(List<Location> subLocations)Location(Location[] subLocations)构造函数对此案例没有帮助。
  3. 注意:我无法控制JSON格式,所以我无法以更加杰克逊友好的方式对其进行编码。

1 个答案:

答案 0 :(得分:1)

您需要自定义反序列化器。你不能只是添加一个构造函数。

这是一个包含类Foo的自包含示例,可以由其自己的属性"foo" : "someString"或某个逻辑运算符"and""or"表示等等作为String字面值,旨在表示Foo属性为{1}}属性的foo实例。

这可能或可能不完全适合您的情况,但您可以调整。

换句话说:

  • {"foo": "a"} - &gt; new Foo("a")
  • "or" - &gt; new Foo("or")

示例

// 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));

<强>输出

a
or
b
为清晰起见,

注意

这代表了一个非常简单的例子。 在您的情况下,您可能希望将Location POJO与LogicalOperator POJO(或类似的东西)混合的多态集合,共享一个公共标记接口。 然后,您可以根据JSON节点是否具有内容(即位置)或JSON节点 其内容(例如逻辑运算符)来决定要反序列化的对象。