jackson空字符串处理问题

198 阅读1分钟

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));
            }
        });
    }
}