使用 Jackson 反序列化 Java 个数组

Deserializing Java arrays with Jackson

我正在尝试使用 Jackson 作为通用序列化引擎而不是 Java 序列化。 通过以下方式初始化映射器:

objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

所有类型信息都存储在 JSON 中,因此我可以通过以下方式写入和读取我的所有对象:

objectMapper.readValue(json, Object.class)

我在尝试序列化然后反序列化 java 数组时遇到问题。 由于 Jackson 没有将数组类型存储到 JSON 中,因此它在反序列化阶段稍后会失败。在以下代码中:

    String [] strings = {"A", "B", "C"};
    try {
        String json = objectMapper.writeValueAsString(strings);
        String [] stringsBack = (String [])objectMapper.readValue(json, Object.class);
        if (!strings.equals(stringsBack)) {
            System.err.println("ERROR, stringsBack not the same!!!\n\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

json 将设置为“["A","B","C"]”但是在反序列化时我得到以下异常:

Exception in thread "main" java.lang.IllegalArgumentException: Invalid type id 'A' (for id type 'Id.class'): no such class found
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:66)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:48)
at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:157)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:94)
at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:68)
at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserializeWithType(UntypedObjectDeserializer.java:494)
at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2576)
at Main.run(Main.java:86)

有没有办法指示 Jackson 将 java 数组类型信息也存储到 JSON 中? 我的序列化引擎是通用的,并且事先不知道它将从 JSON 字符串中读取的类型。

我目前找到的最佳解决方案是在序列化之前检查对象是否为数组 (Class.isArray),然后进行序列化:

TypeReference ref = new TypeReference<List[]>() { };
String json = objectMapper.writerFor(ref).writeValueAsString(strings);

这会将数组类型添加到 JSON 中并允许读取它。

正确的方法是:

String[] strings = { "A", "B", "C" };
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(strings);
List<String> data = mapper.readValue(json,
        mapper.getTypeFactory().constructCollectionType(List.class, String.class));
for (String string : data) {
    System.out.println(string);
}

代码存在多个问题。

1) 首先让我们看看为什么会出现异常

Exception in thread "main" java.lang.IllegalArgumentException: Invalid type id 'A' (for id type 'Id.class'): no such class found 

上面的错误表明它没有找到一些 Class 并将 A 称为无效类型 ID 'A',这似乎是 Json 中的一个问题。

问题是因为行

objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

这为所有非最终类型启用了默认类型。默认类型打印 Object 的类型和 Object 的实际值,因此可以反序列化为相同的类型。声明它 NON_FINAL 不会添加 "natural" 类型,例如 String.

String [] stringsBack = (String [])objectMapper.readValue(json, Object.class);

现在反序列化 Json ["A"、"B"、"C"] 时,反序列化器不知道 json 要转换的对象也将 A 作为 Class 类型并试图找到它没有得到的 class。

2) 评论第一行可能会给出 ClassCastException 因为

objectMapper.readValue(json, Object.class);

会 return 一个 ArrayList 的对象,它不能转换为 String[]。

3) !strings.equals(stringsBack) 将始终 return 为真,因为 strings 和 stringsBack 都是 String[] 的不同对象。

更正以下代码中的所有内容即可

    ObjectMapper objectMapper = new ObjectMapper();
    //You can remove the below line if Default Typing is not Required.  
    objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    String[] strings = { "A", "B", "C" };
    try
    {
        String json = objectMapper.writeValueAsString(strings);
        System.out.println(json);
        String[] stringsBack = (String[]) objectMapper.readValue(json, new TypeReference<String[]>()
        {});
        if (!Arrays.equals(strings, stringsBack))
        {
            System.err.println("ERROR, stringsBack not the same!!!\n\n");
        }
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }