杰克逊xml 2.9.0:@JacksonXmlElementWrapper无法与@JsonCreator& @JsonProperty构造函数 [英] Jackson xml 2.9.0: @JacksonXmlElementWrapper not working with @JsonCreator & @JsonProperty constructor

查看:87
本文介绍了杰克逊xml 2.9.0:@JacksonXmlElementWrapper无法与@JsonCreator& @JsonProperty构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望我的ParentClass具有final字段,'brokenChildList'列表包装了xml元素,并且列表项的标签与列表(<brokenChildList><brokenChild/></brokenChildList>)不同.

I would like that my ParentClass has final fields, 'brokenChildList' list is wrapped xml element and list items have different tag than the list (<brokenChildList><brokenChild/></brokenChildList>).

这里是一段代码,用于重现我的问题(部分导入被截断,省略了setter和getter)

Here is a snippet of code to reproduce my issues (imports are partially truncated, setters and getters omitted)

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public class Main {
    public static void main(String... args) throws IOException {
        ObjectMapper xmlMapper = new XmlMapper();
        String xmlString = "<ParentClass><childClass name=\"name1\" value=\"val1\"/><brokenChildList><brokenChild name=\"bc1\" reason=\"bc-val1\"/><brokenChild name=\"bc2\" reason=\"bc-val2\"/></brokenChildList></ParentClass>";
        ParentClass parentClass = xmlMapper.readValue(xmlString, ParentClass.class);
        StringWriter stringWriter = new StringWriter();
        xmlMapper.writeValue(stringWriter, parentClass);
        String serialised = stringWriter.toString();
        System.out.println(serialised);
        System.out.println(xmlString.equals(serialised));
    }

    public static class ChildClass {
        @JacksonXmlProperty(isAttribute = true)
        private String name;
        @JacksonXmlProperty(isAttribute = true)
        private String value;
        //getters & setters
    }
    public static class BrokenChild {
        @JacksonXmlProperty(isAttribute = true)
        private String name;
        @JacksonXmlProperty(isAttribute = true)
        private String reason;
        //getters & setters
    }
    public static class ParentClass {
        private final ChildClass childClass;
        private final List<BrokenChild> brokenChildList;
        @JsonCreator
        public ParentClass(
            @JsonProperty("childClass") ChildClass childClass,
            @JsonProperty("brokenChildList") List<BrokenChild> brokenChildList
        ) {
            this.childClass = childClass;
            this.brokenChildList = brokenChildList;
        }

        @JacksonXmlProperty(localName = "childClass")
        public ChildClass getChildClass() {
            return childClass;
        }

        @JacksonXmlElementWrapper(localName = "brokenChildList")
        @JacksonXmlProperty(localName = "brokenChild")
        public List<BrokenChild> getBrokenChildList() {
            return brokenChildList;
        }
    }
}

上面的代码给出了Jackson版本2.8.10的输出:

The above code gives output with Jackson version 2.8.10:

<ParentClass><childClass name="name1" value="val1"/><brokenChildList><brokenChild name="bc1" reason="bc-val1"/><brokenChild name="bc2" reason="bc-val2"/></brokenChildList></ParentClass>
true

对于Jackson版本2.9.0,它会给出:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Duplicate property 'brokenChildList' for [simple type, class org.test.Main$ParentClass]
 at [Source: (StringReader); line: 1, column: 1]

我想找到一个解决方案(以及2.9.0之后的任何版本),该解决方案将提供与附带代码相同的输出.

I would like to find a solution (and any version after 2.9.0) that will give same output with the attached code.

我失败的尝试包括:

  • @JacksonXmlElementWrapper替换@JacksonXmlElementWrapper(localName = "brokenChildList")会将包装器元素重命名为"brokenChild",这是不希望的.

  • Replacing @JacksonXmlElementWrapper(localName = "brokenChildList") with @JacksonXmlElementWrapper will rename wrapper element as 'brokenChild' which is undesirable.

删除@JacksonXmlElementWrapper(localName = "brokenChildList")会将包装器元素重命名为'brokenChild',这是不希望的.

Removing @JacksonXmlElementWrapper(localName = "brokenChildList") will rename wrapper element as 'brokenChild' which is undesirable.

推荐答案

这个问题真的很棘手,因为Jackson从不同的地方收集元数据:字段,getter,setter,构造函数参数.另外,您可以使用MixIn,但在您的情况下不会出现.

This problem is really tricky because Jackson collects metadata from different places: fields, getters, setters, constructor parameters. Also, you can use MixIn but in your case it does not appear.

@JacksonXmlElementWrapper批注附加到FIELDMETHOD类型元素上,这将强制您在getter上声明它.因为ParentClass是不可变的,并且您想使用构造函数构建它,所以我们还需要注释构造函数参数.这就是发生冲突的地方:您有一个带有@JsonProperty("brokenChildList")批注的构造函数参数和一个带有@JacksonXmlElementWrapper(localName = "brokenChildList")的getter,该参数重用了相同的名称.如果将localName更改为@JacksonXmlElementWrapper(localName = "brokenChildListXYZ")(添加了XYZ),则所有内容都将反序列化和序列化,但输出将不同于输入.

@JacksonXmlElementWrapper annotation can be attached to FIELD and METHOD type elements and this forces you to declare it on getter. Because ParentClass is immutable and you want to build it with constructor we need to annotate constructor parameters as well. And this is where collision appears: you have a constructor parameter with @JsonProperty("brokenChildList") annotation and getter with @JacksonXmlElementWrapper(localName = "brokenChildList") which reuses the same name. If you would changed localName to @JacksonXmlElementWrapper(localName = "brokenChildListXYZ") (added XYZ) everything would be deserialised and serialised but output would be different then input.

要解决此问题,我们可以使用com.fasterxml.jackson.databind.deser.BeanDeserializerModifier类,该类允许过滤出我们不想用于反序列化的字段,并且会产生冲突.用法示例:

To solve this problem, we can use com.fasterxml.jackson.databind.deser.BeanDeserializerModifier class which allows to filter out fields we do not want to use for deserialisation and which creates collision. Example usage:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.stream.Collectors;

public class XmlMapperApp {

    public static void main(String... args) throws IOException {
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config, BeanDescription beanDesc, List<BeanPropertyDefinition> propDefs) {
                if (beanDesc.getBeanClass() == ParentClass.class) {
                    return propDefs.stream().filter(p -> p.getConstructorParameter() != null).collect(Collectors.toList());
                }
                return super.updateProperties(config, beanDesc, propDefs);
            }
        });
        XmlMapper xmlMapper = XmlMapper.xmlBuilder()
                .addModule(module)
                .build();

        //yours code
    }
}

要创建此示例,我使用了版本2.10.0.

To create this example I used version 2.10.0.

另请参阅:

  • Jackson 2.10 features
  • Jackson Release 2.10

这篇关于杰克逊xml 2.9.0:@JacksonXmlElementWrapper无法与@JsonCreator&amp; @JsonProperty构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆