当前位置: 首页 > 知识库问答 >
问题:

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

殷德本
2023-03-14

我有以下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的注释,但没有成功。

共有3个答案

于鹏
2023-03-14

如果有人仍然面临此问题,请升级到最新的jackson 2.12.3版本。我在2.11.x版本中遇到了这个问题。

周和安
2023-03-14

我明白为什么史蒂芬·哈兹拉的答案被否决了,因为他并没有真正回答这个问题:他是对的。

我不知道确切的版本,但直到2.11. x xml中数组的自定义反序列化被破坏。如果我们从问题中获取xml并实现一个反序列化器并像这样使用它:

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

如果MyDeserializer类从StdSerializer扩展,也像问题中一样,反序列化方法总是只从要处理的条目的子项中获取最后一个子“条目”元素。

在以后的版本中,这是固定的,您现在可以实现反序列化方法,如下所示:

@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解析内容是不够的,因为那样我就丢失了基于孩子原始标签名的重要信息。最终,我使用的杰克逊2.11版本在这方面被破坏了。感谢Stephen为我指明了方向。

金高飞
2023-03-14

它似乎不支持获取整个集合。

我在没有自定义反序列化器的情况下做了一些工作。

@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));
}
 类似资料:
  • 我有一个lib枚举,如下所示: 我不能修改这个枚举。我需要从xml反序列化,如下所示: 我知道如何创建和使用混搭与杰克逊。但是如何告诉杰克逊使用外部函数进行反序列化呢?

  • 我有下面的JSON,我正试图使用Jackson API反序列化它 我基本上需要一个附件类,它有一个AttachmentFile对象列表,如下所示: 如何使用自定义反序列化器实现这一点? 谢谢

  • I'va是一个OID接口,可以由许多具体类型实现: 现在我有一个具有两个字段的对象,一个使用抽象接口类型(OID)定义,另一个使用具体类型(MyOID)定义 我想使用jackson以不同的方式序列化/反序列化字段,无论它们是使用抽象接口类型还是具体类型定义的: 注意,被序列化,包括类型信息(多态序列化),而被序列化为文本 为此,我将OID接口注释为: 并为每个具体类型分配了类型id: 最后,对容器

  • 我想反序列化表单中的类: 其中文本是加密的,反序列化应该在重建TestFieldEncryptedMessage实例之前取消对值的加密。 我采用的方法非常类似于:https://github.com/codesqueak/jackson-json-crypto 也就是说,我正在构建一个扩展SimpleModule的模块: 如您所见,设置了两个修饰符:EncryptedSerializerModif

  • 我正在尝试为泛型列表创建一个自定义反序列化器。假设我得到了类B的json表示: 其中A是我仅在运行时看到的其他类。我想创建一个反序列化程序,该程序能够将listObject的类型推断为具有内部类型a的list,并将其反序列化,而不是使用默认的hashmap反序列化程序。 我尝试使用上下文反序列化器,类似于这里建议的,然后将其添加为List的自定义反序列化器 但是我不确定我应该如何读取json并在反

  • 我想通过扩展默认的反序列化器来创建自己的反序列化器,在其后面设置更多的值: 如您所见,我还想将此DTO母类重用于其他DTO。 我没有找到任何这样的例子。我真的是世界上第一个 反序列化的“AsUsual”(p,ctxt)应该是什么 我应该使用什么motherclass?JsonDeserializer/StdDeserializer/UntypedObjectDeserializer 反序列化程序会