【问题标题】:Jackson xml 2.9.0: @JacksonXmlElementWrapper not working with @JsonCreator & @JsonProperty constructorJackson xml 2.9.0:@JacksonXmlElementWrapper 不能与 @JsonCreator 和 @JsonProperty 构造函数一起使用
【发布时间】:2020-03-26 05:21:42
【问题描述】:

我希望我的ParentClassfinal 字段,'brokenChildList' 列表是包装的 xml 元素,并且列表项的标记与列表 (<brokenChildList><brokenChild/></brokenChildList>) 不同。

这是重现我的问题的代码 sn-p(导入被部分截断,省略了 setter 和 getter)

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的输出:

<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 之后的任何版本),它可以提供与附加代码相同的输出。

我失败的尝试包括:

  • @JacksonXmlElementWrapper(localName = "brokenChildList") 替换为 @JacksonXmlElementWrapper 会将包装器元素重命名为“brokenChild”,这是不可取的。

  • 删除 @JacksonXmlElementWrapper(localName = "brokenChildList") 会将包装器元素重命名为“brokenChild”,这是不可取的。

【问题讨论】:

    标签: java xml jackson xml-deserialization jackson-dataformat-xml


    【解决方案1】:

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

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

    为了解决这个问题,我们可以使用com.fasterxml.jackson.databind.deser.BeanDeserializerModifier 类,它允许过滤掉我们不想用于反序列化的字段并产生冲突。用法示例:

    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

    另见:

    【讨论】:

    • 谢谢。我为我迟到的回复道歉。这适用于示例代码,我还在我的实际应用程序中实现了这个解决方案。如果可以在全球范围内实施,那就更好了。现在我必须使用“if”语句来定位多个不可变类。将集合以 xml 表示为 &lt;items&gt;&lt;items/&gt;&lt;items/&gt;&lt;/items&gt;&lt;items&gt;&lt;item/&gt;&lt;item/&gt;&lt;/items&gt; 是否更传统?基于这个解决方案的不直截了当,我怀疑我是非常规的。
    • @VadaVad, &lt;items&gt;&lt;item/&gt;&lt;item/&gt;&lt;/items&gt; 看起来很自然,我想。解决方案仍然使用库中的所有内部功能,所以没关系。有些问题很复杂,只能通过注释来解决。
    猜你喜欢
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 2014-03-22
    • 2016-09-04
    • 1970-01-01
    • 2022-01-13
    相关资源
    最近更新 更多