xmlMapper 允许在反序列化期间使用任何根元素
xmlMapper allow to use any root element during deserialization
我有这样的代码
public class Xml {
public static void main(String[] args) throws JsonProcessingException {
String xmlString = "<password><plainPassword>12345</plainPassword></password>";
XmlMapper xmlMapper = new XmlMapper();
PlainPassword plainPassword = xmlMapper.readValue(xmlString, PlainPassword.class);
System.out.println(plainPassword.getPlainPassword());
}
@JacksonXmlRootElement(localName = "password")
public static class PlainPassword {
public String getPlainPassword() {
return this.plainPassword;
}
public void setPlainPassword(String plainPassword) {
this.plainPassword = plainPassword;
}
private String plainPassword;
}
}
它工作正常,但在 xmlString
中我可以使用任何根标签名称并且我的代码仍然可以工作。
例如 String xmlString = "<x><plainPassword>12345</plainPassword></x>";
我使用 x
作为根元素也可以。
但是是否可以说 xmlMapper 只能正确反序列化具有“密码”根元素的字符串?
您可以将 root class 的名称更改为任何内容,例如:@JacksonXmlRootElement(localName = "xyz")
并且它有效。
基于 Java documentation JacksonXmlRootElement 用于定义用于 root-level 对象的根元素的名称 序列化时(不适用于反序列化映射),通常使用类型名称 (class).
我会以不同的方式处理这个问题。获取 XPath 实现,select 所有匹配 //plainPassword
的节点,然后获取每个节点的内容列表。
如果需要,还可以获取父节点的名称;在找到的节点的上下文中使用 ..
获取父节点。
检查 XPath examples 并亲自尝试。请注意,您的代码可能因语言和 XPath 实现而异。
不幸的是,您描述的行为是 Jackson 支持的行为,如 this Github open issue 中所述。
使用 JSON 内容和 ObjectMapper
你可以启用 UNWRAP_ROOT_VALUE
反序列化功能,也许它可以帮助实现这个目的,虽然我不太确定这个功能是否XmlMapper
.
是否正确支持
一种可能的解决方案是实施自定义解串器。
鉴于你的 PlainPassword
class:
@JacksonXmlRootElement(localName = "password")
public class PlainPassword {
public String getPlainPassword() {
return this.plainPassword;
}
public void setPlainPassword(String plainPassword) {
this.plainPassword = plainPassword;
}
private String plainPassword;
}
考虑以下 main
方法:
public static void main(String[] args) throws JsonProcessingException {
String xmlString = "<x><plainPassword>12345</plainPassword></x>";
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
Class<?> beanClass = beanDesc.getBeanClass();
JacksonXmlRootElement annotation = beanClass.getAnnotation(JacksonXmlRootElement.class);
String requiredLocalName = null;
if (annotation != null) {
requiredLocalName = annotation.localName();
}
if (requiredLocalName != null) {
return new EnforceXmlElementNameDeserializer<>(deserializer, beanDesc.getBeanClass(), requiredLocalName);
}
return deserializer;
}
}));
PlainPassword plainPassword = xmlMapper.readValue(xmlString, PlainPassword.class);
System.out.println(plainPassword.getPlainPassword());
}
自定义解串器的样子:
public class EnforceXmlElementNameDeserializer<T> extends StdDeserializer<T> implements ResolvableDeserializer {
private final JsonDeserializer<?> defaultDeserializer;
private final String requiredLocalName;
public EnforceXmlElementNameDeserializer(JsonDeserializer<?> defaultDeserializer, Class<?> beanClass, String requiredLocalName) {
super(beanClass);
this.defaultDeserializer = defaultDeserializer;
this.requiredLocalName = requiredLocalName;
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
if (!this.requiredLocalName.equals(rootName)) {
throw new IllegalArgumentException(
String.format("Root name '%s' does not match required element name '%s'", rootName, this.requiredLocalName)
);
}
@SuppressWarnings("unchecked")
T itemObj = (T) defaultDeserializer.deserialize(p, ctxt);
return itemObj;
}
@Override public void resolve(DeserializationContext ctxt) throws JsonMappingException {
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
}
修改BeanDeserializer
时必须实现ResolvableDeserializer
,否则反序列化会抛出异常。
代码基于 。
测试应引发 IllegalArgumentException
并显示相应的消息:
Root name 'x' does not match required element name 'password'
请根据需要修改异常类型。
如果您使用:
String xmlString = "<password><plainPassword>12345</plainPassword></password>";
在您的 main
方法中,它应该 运行 没有问题。
我有这样的代码
public class Xml {
public static void main(String[] args) throws JsonProcessingException {
String xmlString = "<password><plainPassword>12345</plainPassword></password>";
XmlMapper xmlMapper = new XmlMapper();
PlainPassword plainPassword = xmlMapper.readValue(xmlString, PlainPassword.class);
System.out.println(plainPassword.getPlainPassword());
}
@JacksonXmlRootElement(localName = "password")
public static class PlainPassword {
public String getPlainPassword() {
return this.plainPassword;
}
public void setPlainPassword(String plainPassword) {
this.plainPassword = plainPassword;
}
private String plainPassword;
}
}
它工作正常,但在 xmlString
中我可以使用任何根标签名称并且我的代码仍然可以工作。
例如 String xmlString = "<x><plainPassword>12345</plainPassword></x>";
我使用 x
作为根元素也可以。
但是是否可以说 xmlMapper 只能正确反序列化具有“密码”根元素的字符串?
您可以将 root class 的名称更改为任何内容,例如:@JacksonXmlRootElement(localName = "xyz")
并且它有效。
基于 Java documentation JacksonXmlRootElement 用于定义用于 root-level 对象的根元素的名称 序列化时(不适用于反序列化映射),通常使用类型名称 (class).
我会以不同的方式处理这个问题。获取 XPath 实现,select 所有匹配 //plainPassword
的节点,然后获取每个节点的内容列表。
如果需要,还可以获取父节点的名称;在找到的节点的上下文中使用 ..
获取父节点。
检查 XPath examples 并亲自尝试。请注意,您的代码可能因语言和 XPath 实现而异。
不幸的是,您描述的行为是 Jackson 支持的行为,如 this Github open issue 中所述。
使用 JSON 内容和 ObjectMapper
你可以启用 UNWRAP_ROOT_VALUE
反序列化功能,也许它可以帮助实现这个目的,虽然我不太确定这个功能是否XmlMapper
.
一种可能的解决方案是实施自定义解串器。
鉴于你的 PlainPassword
class:
@JacksonXmlRootElement(localName = "password")
public class PlainPassword {
public String getPlainPassword() {
return this.plainPassword;
}
public void setPlainPassword(String plainPassword) {
this.plainPassword = plainPassword;
}
private String plainPassword;
}
考虑以下 main
方法:
public static void main(String[] args) throws JsonProcessingException {
String xmlString = "<x><plainPassword>12345</plainPassword></x>";
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
Class<?> beanClass = beanDesc.getBeanClass();
JacksonXmlRootElement annotation = beanClass.getAnnotation(JacksonXmlRootElement.class);
String requiredLocalName = null;
if (annotation != null) {
requiredLocalName = annotation.localName();
}
if (requiredLocalName != null) {
return new EnforceXmlElementNameDeserializer<>(deserializer, beanDesc.getBeanClass(), requiredLocalName);
}
return deserializer;
}
}));
PlainPassword plainPassword = xmlMapper.readValue(xmlString, PlainPassword.class);
System.out.println(plainPassword.getPlainPassword());
}
自定义解串器的样子:
public class EnforceXmlElementNameDeserializer<T> extends StdDeserializer<T> implements ResolvableDeserializer {
private final JsonDeserializer<?> defaultDeserializer;
private final String requiredLocalName;
public EnforceXmlElementNameDeserializer(JsonDeserializer<?> defaultDeserializer, Class<?> beanClass, String requiredLocalName) {
super(beanClass);
this.defaultDeserializer = defaultDeserializer;
this.requiredLocalName = requiredLocalName;
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
String rootName = ((FromXmlParser)p).getStaxReader().getLocalName();
if (!this.requiredLocalName.equals(rootName)) {
throw new IllegalArgumentException(
String.format("Root name '%s' does not match required element name '%s'", rootName, this.requiredLocalName)
);
}
@SuppressWarnings("unchecked")
T itemObj = (T) defaultDeserializer.deserialize(p, ctxt);
return itemObj;
}
@Override public void resolve(DeserializationContext ctxt) throws JsonMappingException {
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
}
修改BeanDeserializer
时必须实现ResolvableDeserializer
,否则反序列化会抛出异常。
代码基于
测试应引发 IllegalArgumentException
并显示相应的消息:
Root name 'x' does not match required element name 'password'
请根据需要修改异常类型。
如果您使用:
String xmlString = "<password><plainPassword>12345</plainPassword></password>";
在您的 main
方法中,它应该 运行 没有问题。