jdk升级高版本后,jackson也从2.9.x升级到了2.17.x,在测试中发现新老版本xml反序列化时,对空元素处理不一致.老版本反序列化为null,新版本发序列化为""。这导致输出的api返回结果不一致,需要把空字符串处理为null。
现象
把xml反序列化为java对象时,对空元素处理不一致。 jackson版本
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.17.1</version>
xml示例
<?xml version="1.0" encoding="UTF-8"?><rsp><name></name><id></id><o></o></rsp>
对应的java类
@XmlRootElement(
name = "rsp"
)
@Data
static class TestObject {
@XmlElement(
name = "name"
)
String name;
@XmlElement(
name = "id"
)
int id;
@XmlElement(
name = "o"
)
Object o;
}
public void testXml() throws IOException {
String ss = "<?xml version="1.0" encoding="UTF-8"?><rsp><name></name><id></id><o></o></rsp>";
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL);
SimpleModule module = new NullToEmptyStringModule();
xmlMapper.registerModule(module);
TestObject testObject = xmlMapper.readValue(ss, TestObject.class);
System.out.println(JSON.toJSONString(testObject));
TestObject testObject1 = XmlMapper.builder()
.enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL)
.enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL).build().readValue(ss, TestObject.class);
System.out.println(JSON.toJSONString(testObject1));
}
原因
jackson高版本中,xml规范和空元素默认为“”空字符串,但是对可以设置xmlMapper.enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL);当作Null处理 相见源码测试类注释
EmptyStringValueTest.java
public void testEmptyElement() throws Exception
{
final String XML = "<name><first/><last></last></name>";
// Default settings (since 2.12): empty element does NOT become `null`:
Name name = MAPPER.readValue(XML, Name.class);
assertNotNull(name);
assertEquals("", name.first);
assertEquals("", name.last);
// but can be changed
XmlMapper mapper2 = XmlMapper.builder()
.enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL)
.build();
name = mapper2.readValue(XML, Name.class);
assertNotNull(name);
assertNull(name.first);
assertEquals("", name.last);
}
解决办法
自定义反序列化module,对于空元素处理为null
static class NullToEmptyStringModule extends SimpleModule {
protected NullToEmptyStringModule() {
this.addDeserializer(String.class, new JsonDeserializer<String>() {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
if(StringUtils.isEmpty(p.getText())){
return null;
}
return p.getText();}
});
}
}
把自定义module注册到XmlMapper中
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL);
SimpleModule module = new NullToEmptyStringModule();
xmlMapper.registerModule(module);
TestObject testObject = xmlMapper.readValue(ss, TestObject.class);
System.out.println(JSON.toJSONString(testObject));
类似的有序列化时date类型处理,自定义module,并且注册到ObjectMapper中
public class CustomModule extends SimpleModule {
private static final long serialVersionUID = 1L;
/**
* Custom Jackson Module to serialize ZoneDateTime into desired format.
*/
public CustomModule() {
this.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
@Override
public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime));
}
});
}
}