Jackson xml 2.9.0:@JacksonXmlElementWrapper 无法与@JsonCreator 和@JsonProperty 构造函数一起使用
Jackson xml 2.9.0: @JacksonXmlElementWrapper not working with @JsonCreator & @JsonProperty constructor
我希望我的 ParentClass
有 final
个字段,'brokenChildList' 列表被包装 xml 元素和列表项具有与列表不同的标签(<brokenChildList><brokenChild/></brokenChildList>
).
这是重现我的问题的代码片段(导入被部分截断,省略了 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',这是不可取的。
这个问题真的很棘手,因为 Jackson
从不同的地方收集元数据:字段、getters、设置器、构造函数参数。此外,您可以使用 MixIn
但在您的情况下它不会出现。
@JacksonXmlElementWrapper
注释可以附加到 FIELD
和 METHOD
类型元素,这会强制您在 getter 上声明它。因为 ParentClass
是不可变的并且你想用构造函数构建它,我们还需要注释构造函数参数。这就是出现冲突的地方:您有一个带有 @JsonProperty("brokenChildList")
注释的构造函数参数和 getter 带有 @JacksonXmlElementWrapper(localName = "brokenChildList")
的重用相同名称的构造函数参数。如果您将 localName
更改为 @JacksonXmlElementWrapper(localName = "brokenChildListXYZ")
(添加 XYZ
),所有内容都将被反序列化和序列化,但输出将与输入不同。
为了解决这个问题,我们可以使用 com.fasterxml.jackson.databind.deser.BeanDeserializerModifier
class 它允许过滤掉我们不想用于反序列化的字段,这会产生冲突。用法示例:
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
。
另请参阅:
我希望我的 ParentClass
有 final
个字段,'brokenChildList' 列表被包装 xml 元素和列表项具有与列表不同的标签(<brokenChildList><brokenChild/></brokenChildList>
).
这是重现我的问题的代码片段(导入被部分截断,省略了 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',这是不可取的。
这个问题真的很棘手,因为 Jackson
从不同的地方收集元数据:字段、getters、设置器、构造函数参数。此外,您可以使用 MixIn
但在您的情况下它不会出现。
@JacksonXmlElementWrapper
注释可以附加到 FIELD
和 METHOD
类型元素,这会强制您在 getter 上声明它。因为 ParentClass
是不可变的并且你想用构造函数构建它,我们还需要注释构造函数参数。这就是出现冲突的地方:您有一个带有 @JsonProperty("brokenChildList")
注释的构造函数参数和 getter 带有 @JacksonXmlElementWrapper(localName = "brokenChildList")
的重用相同名称的构造函数参数。如果您将 localName
更改为 @JacksonXmlElementWrapper(localName = "brokenChildListXYZ")
(添加 XYZ
),所有内容都将被反序列化和序列化,但输出将与输入不同。
为了解决这个问题,我们可以使用 com.fasterxml.jackson.databind.deser.BeanDeserializerModifier
class 它允许过滤掉我们不想用于反序列化的字段,这会产生冲突。用法示例:
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
。
另请参阅: