Jackson自定义反序列化程序仅获取列表xml中的最后一个值

h9a6wy2h  于 2022-11-08  发布在  其他
关注(0)|答案(3)|浏览(253)

我有以下xml

<root>
<date>112004</date>
<entries>
    <entry id = 1>
        <status>Active</status>
        <person>
            <Name>John</Name>
            <Age>22</Age>
        </person>
    </entry>
    <entry id = 2>
        <status>Active</status>
        <person>
            <Name>Doe</Name>
            <Age>32</Age>
        </person>
    </entry>
    <entry id = 3>
        <status>N/A</status>
    </entry>
</entries>

我使用自定义的Jackson反序列化器来获取值,pojo如下所示:

@JacksonXmlRootElement(localName="root", namespace="namespace")

类根{私有字符串日期;

@JacksonXmlProperty(localName = "entries", namespace="tns")
private List<Entry> entries;
//getter and setter

}

class Entry {
 private String id;
 private String status;
 private Person person;
 //getter and setter
}

反序列化程序代码如下所示

public class DeSerializer extends StdDeserializer<root>
{
 protected DeSerializer() {
    super(root.class);
  }
  @Override
public root deserialize(JsonParser p, DeserializationContext ctxt)  throws IOException, JsonProcessingException {
    JsonNode nodes = p.readValueAsTree();
    ObjectMapper mapper = new ObjectMapper();
    List<Entry> entry = mapper.convertValue(nodes.findValues("entry"), new TypeReference<List<Entry>>() {});
}
}

main()
{
XmlMapper x = new XmlMapper();
 final SimpleModule module = new SimpleModule("configModule",   com.fasterxml.jackson.core.Version.unknownVersion());
            module.addDeserializer(root.class, new DeSerializer());
            x.registerModule(module);
            root r = x.readValue(xmlSource, root.class); /*xmlsource is xml as string*/
}

问题是当我调试时,我总是从xml中获取条目的最后一个值。因此,节点的值(在反序列化器中)是{“date”:“112004”,“entries”:{“entry”:{“id”:“3”,“status”:“N/A”}}},我不确定为什么它不被视为列表。我确实为列表添加了unwrapped = false注解,但没有成功。

dbf7pr2w

dbf7pr2w1#

readValueAsTree似乎不支持提取整个集合。
我做了一些工作,没有自定义DeSerializer的工作。

@JacksonXmlRootElement(localName="root")
public class Root {
@JacksonXmlElementWrapper(useWrapping = true)
private List<Entry> entries;

private String date;

public List<Entry> getEntries() {
    return entries;
}

public void setEntries(List<Entry> entries) {
    if (this.entries == null){
        this.entries = new ArrayList<>(entries.size());
    }
    this.entries.addAll(entries);
}

public String getDate() {
    return date;
}

public void setDate(String date) {
    this.date = date;
}
}

class Entry {
private String id;
private String status;
private Person person;
}

class Person {
@JacksonXmlProperty(localName = "Name")
private String name;

@JacksonXmlProperty(localName = "Age")
private String age;
}

然后进行单元测试:

@Test
    void test_xml_XmlMapper() throws Exception {
    JacksonXmlModule xmlModule = new JacksonXmlModule();
    xmlModule.setDefaultUseWrapper(false);
    ObjectMapper xmlMapper = new XmlMapper(xmlModule);

    String xmlContent = "your xml file here"
    Root bean = xmlMapper.readValue(xmlContent, Root.class);
    assertThat(bean.getEntries().size(), Matchers.equalTo(3));
}
z18hc3ub

z18hc3ub2#

我能理解为什么@斯蒂芬-哈扎拉的答案被否决了,因为他并没有真正回答这个问题,但是:他说得对。
我不知道确切的版本,但直到2.11.x,xml中数组的自定义反序列化被破坏了。如果我们从问题中取出xml,实现一个反序列化器,并像这样使用它:

@JsonDeserialize(using = MyDeserializer.class)
private List<MyClass> entries = new ArrayList<>();

如果MyDeserializer类是从StdSerializer扩展而来的,也像在问题中一样,则反序列化方法将始终只从要处理的条目的子级中获取最后一个子“entry”元素。
在以后的版本中,此问题已得到修复,现在可以按如下方式实现反序列化方法:

@Override
public List<MyClass> deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
    final List<MyClass> result = new ArrayList<>();
    final ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
    //entry and extendedEntry are different children in the xml that can be parsed as MyClass
    processNode((ContainerNode) node.get("entry"), jsonParser, result);
    processNode((ContainerNode) node.get("extendedEntry"), jsonParser, result);
    return result;
}

private void processNode(ContainerNode cNode, JsonParser jsonParser, List<MyClass> result) throws IOException {
    if (cNode == null) return;
    //cNode can either be an ObjectNode or an ArrayNode based on how many children there can be found in the xml.
    if (cNode.isArray()) { cNode.iterator().forEachRemaining(node -> {
        try {
            result.add(node.traverse(jsonParser.getCodec()).readValueAs(MyClass.class));
            } catch (IOException e) {
              //whatever
            }
        });
    } else {
        result.add(cNode.traverse(jsonParser.getCodec()).readValueAs(MyClass.class));
    }
}

我写下这个例子作为答案,因为我整个下午都在思考如何处理xml中的其他子元素,因为在我的例子中,xml中的子元素具有不同的标记名称,但具有相同的内容类型,因此仅使用contentUsing解析内容是不够的。因为这样我就丢失了基于孩子的原始标签名称的重要信息。最后我发现我使用的是Jackson2.11版本。在这方面我刚刚被打破了。所以感谢斯蒂芬给我指出了这个方向。

mrfwxfqh

mrfwxfqh3#

如果有人仍然面临这个问题,请升级到最新的Jackson2. 12. 3版本。我在2. 11. x版本中就面临过这个问题。

相关问题