Java Jackson xml 2.9.0:@JacksonXmlElementWrapper不使用@JsonCreator和@JsonProperty构造函数

Java Jackson xml 2.9.0:@JacksonXmlElementWrapper不使用@JsonCreator和@JsonProperty构造函数,java,xml,jackson,xml-deserialization,jackson-dataformat-xml,Java,Xml,Jackson,Xml Deserialization,Jackson Dataformat Xml,我希望我的父类具有最终字段,“brokenChildList”列表是包装的xml元素,列表项具有与列表不同的标记 下面是一段代码来重现我的问题导入部分被截断,setter和getter被省略 import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMap

我希望我的父类具有最终字段,“brokenChildList”列表是包装的xml元素,列表项具有与列表不同的标记

下面是一段代码来重现我的问题导入部分被截断,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.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之后的任何版本,将提供与所附代码相同的输出

我失败的尝试包括:

将@JacksonXmlElementWrapperlocalName=brokenChildList替换为@JacksonXmlElementWrapper将包装器元素重命名为“brokenChild”,这是不可取的

删除@JacksonXmlElementWrapperlocalName=brokenChildList会将包装器元素重命名为“brokenChild”,这是不可取的


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

@JacksonXmlElementWrapper注释可以附加到字段和方法类型元素,这迫使您在getter上声明它。因为父类是不可变的,并且您希望使用构造函数构建它,所以我们还需要注释构造函数参数。这就是冲突出现的地方:您有一个带有@JsonPropertybrokenChildList注释的构造函数参数和一个使用相同名称的@JacksonXmlElementWrapperlocalName=brokenChildList的getter。如果您将localName更改为@jacksonxmlementwrapperlocalname=brokenChildListXYZ,则所有内容都将被反序列化和序列化,但输出与输入不同

要解决此问题,我们可以使用com.fasterxml.jackson.databind.deser.beandSerializerModifier类,该类允许筛选出我们不希望用于反序列化的字段,并创建冲突。用法示例:

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表示为vs是否更为传统?基于这个解决方案的非直截了当性,我怀疑我是在非传统。@VadaVad,我认为看起来很自然。解决方案仍然使用库中的所有内部功能,所以还可以。有些问题很复杂,只有通过注释才能解决。
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
    }
}