java - 当使用 Jackson XmlWrapper 在类里面排名第二时,无法反序列化(转换)未包装的列表

标签 java xml jackson

我正在尝试使用 Jackson 的 XmlMapper 来反序列化一些包含未包装列表的简单 xml 文件。

我的代码:

package zm.study.xmlserialize.jackson;

import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper    ;

public class JacksonListTest {

    public static class A {
        public String c;
        @JacksonXmlElementWrapper(useWrapping=false)
        public List<String> as;
    }

    @Test
    public void deserializeTest() throws Exception
    {
        XmlMapper mapper = new XmlMapper();
        String xml = "<A><c>c</c><as>a1</as><as>a2</as></A>";
        //mapper.readValue(xml, A.class);
        mapper.convertValue(mapper.readTree(xml), A.class);
    }

}

不幸的是,当列表不是类/xml 中的第一个时,库会引发异常。

当我从 xml 和类中删除“c”元素时,异常消失了。 如果我使用 readValue 而不是 convertValue,异常也会消失,但我需要 convertMethod 才能工作。

异常(exception)情况是:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of VALUE_STRING token
 at [Source: (StringReader); line: 1, column: 18] (through reference chain: zm.study.xmlserialize.jackson.JacksonListTest$A["as"])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)

...

最佳答案

我不确定这种方式是否可行。 readTree方法返回扩展 JsonNode 的对象, 在这种情况下它将是 ObjectNode . ObjectNode不接受两个同名属性,最后反序列化后表示:

{"c":"c","as":"a2"}

之后你想把这个节点转换成A POJO类(class)。 List 的默认解串器期待 START_ARRAY token 不是 String .您可以通过实现扩展 StdConverter<String, List> 的自定义转换器来使其工作但列表将被修剪为一个元素。在那种情况下,我认为你必须使用 readValue方法,因为您需要指示 Jacksonas元素是展开的数组。

编辑
在你发表评论后我意识到我们可以欺骗 XmlMapper使用我们想要的任何东西。很明显 Jackson使用 JsonNodeDeserializer反序列化 JsonNode -s。所以,我们需要做的就是找到一个可以注入(inject)代码的地方。幸运的是有一个方法_handleDuplicateField处理我们的案子。默认情况下,如果 FAIL_ON_READING_DUP_TREE_KEY 则抛出异常启用标志:

protected void _handleDuplicateField(JsonParser p, DeserializationContext ctxt,
        JsonNodeFactory nodeFactory,
        String fieldName, ObjectNode objectNode,
        JsonNode oldValue, JsonNode newValue)
    throws JsonProcessingException
{
    // [databind#237]: Report an error if asked to do so:
    if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)) {
        ctxt.reportInputMismatch(JsonNode.class,
                "Duplicate field '%s' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled",
                fieldName);
    }
}

所以,让我们利用这个事实并扩展这个类:

class MergeDuplicateFieldsJsonNodeDeserializer extends JsonNodeDeserializer {
    @Override
    protected void _handleDuplicateField(JsonParser p, DeserializationContext ctxt, 
                                         JsonNodeFactory nodeFactory, String fieldName, ObjectNode objectNode, 
                                         JsonNode oldValue, JsonNode newValue) throws JsonProcessingException {
        super._handleDuplicateField(p, ctxt, nodeFactory, fieldName, objectNode, oldValue, newValue);

        ArrayNode array;
        if (oldValue instanceof ArrayNode) {
            // Merge 3-rd, 4-th, ..., n-th element to already existed array
            array = (ArrayNode) oldValue;
            array.add(newValue);
        } else {
            // Merge first two elements
            array = nodeFactory.arrayNode();
            array.add(oldValue);
            array.add(newValue);
        }
        objectNode.set(fieldName, array);
    }
}

现在,我们需要注册这个解串器。整个测试如下所示:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;

import java.util.Arrays;
import java.util.List;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        A a = new A();
        a.c = "String";
        a.as = Arrays.asList("1", "2", "tom", "Nick");

        SimpleModule mergeDuplicatesModule = new SimpleModule("Merge duplicated fields in array");
        mergeDuplicatesModule.addDeserializer(JsonNode.class, new MergeDuplicateFieldsJsonNodeDeserializer());

        XmlMapper mapper = new XmlMapper();
        mapper.registerModule(mergeDuplicatesModule);

        String xml = mapper.writeValueAsString(a);

        System.out.println(xml);
        System.out.println(mapper.readTree(xml));
    }
}

以上代码打印:

<A><c>String</c><as>1</as><as>2</as><as>tom</as><as>Nick</as></A>
{"c":"String","as":["1","2","tom","Nick"]}

关于java - 当使用 Jackson XmlWrapper 在类里面排名第二时,无法反序列化(转换)未包装的列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54597977/

相关文章:

java - 语音合成空指针异常

java - 如何为 DefaultCellEditor 模拟 "onStartCellEditing"

java - 无法构造 java.util.LinkedHashMap : no String-argument constructor/factory 的实例

java - 如何向我的 JSON 映射器引入转义字符?

java - 使用 Maven 进行 Jenkins Spring Build 失败,但在本地构建时可以工作

java - 从字符串中获取用户名 = 'testuserMM' 的正则表达式

sql-server - 如何将超过 8000 个字符的 XML 字段转换为字符串?

java - 如何避免或重命名 jaxb 中的键和条目标签?

java - 使用同一资源文件更改所有布局的 TextView 值

java - 如何为不可变类创建默认构造函数