使用Jackson XmlWrapper在类中处于第二个状态时,无法反序列化(转换)展开的列表

时间:2019-02-08 18:03:01

标签: 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);
    }

}

不幸的是,当列表不在class / xml中时,库会引发异常。

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

例外是:

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)

...

2 个答案:

答案 0 :(得分:1)

我不确定是否可以这样进行。 readTree方法返回扩展了JsonNode的对象,在这种情况下它将是ObjectNodeObjectNode不接受两个具有相同名称的属性,最后反序列化后,它表示:

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

之后,您希望将此节点转换为A POJO类。 List的默认反序列化程序期望START_ARRAY令牌而不是String。您可以通过实现扩展StdConverter<String, List>的自定义转换器使其工作,但列表将被修剪为一个元素。我认为在这种情况下,您必须使用readValue方法,因为您需要指示Jackson as元素是未包装的数组。

编辑
在您发表评论之后,我意识到我们可以欺骗XmlMapper使用我们想要的任何东西。很明显,Jackson使用JsonNodeDeserializerJsonNode-s进行反序列化。因此,我们要做的就是找到一个可以注入代码的地方。幸运的是,有一种方法_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"]}

答案 1 :(得分:0)

尝试使用以下代码示例

此示例xml文件

<?xml version="1.0" encoding="UTF-8"?>
<UsrAuthentRs>
 <Body>
  <BankUsrInfo>
     <RoleInfo>
        <RoleId>901</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>902</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>903</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>904</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>905</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>906</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>907</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>908</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>909</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>910</RoleId>
     </RoleInfo>
     <RoleInfo>
        <RoleId>911</RoleId>
     </RoleInfo>
  </BankUsrInfo>
  </Body>

package com.inma.itp.queue.models;

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

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

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@XmlRootElement(name = "UsrAuthentRs")
@XmlAccessorType(XmlAccessType.FIELD)
public class UsrAuthentRs {


@XmlElement(name = "Body")
private Body body = new Body();

@Data
public static class Body {
    @XmlElement(name = "BankUsrInfo")
    private BankUsrInfo bankUsrInfo = new BankUsrInfo();


    @Data
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class BankUsrInfo {


        @XmlElement(name = "RoleInfo")
        @JacksonXmlElementWrapper(useWrapping = false)
        private List<RoleInfo> roles;


        @Data
        @NoArgsConstructor
        @XmlAccessorType(XmlAccessType.FIELD)
        public static class RoleInfo {
            @XmlElement(name = "RoleId")
            private String roleId;

        }
    }
}

}

您可以将xml文件转换为Pojo类的主要方法

XmlMapper mapper = new XmlMapper();
UsrAuthentRq deserializedData = xmlMapper.readValue(YouXmlHere, UsrAuthentRq.class);