Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jackson: XML-Binding - Handling Null vs. Empty String

The new Jackson-API provides us with convenient XML-Binding (just like JAXB for example), but i cant find any way to make Jackson serialize the typical "xsi:nil"-Attribute that is defacto standard to represent NULL-Values in XML? Please correct me if i see this wrong ;-)

In JAXB this can be done easily by annotating a java-variable with: @XMLElement(nillable=true)

see also: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html

Can Jackson do this ?

for Jackson-XML see: https://github.com/FasterXML/jackson-dataformat-xml

like image 524
rnd Avatar asked Nov 29 '25 10:11

rnd


1 Answers

I expanded on the work of rnd since it enables the feature for all fields and not just some of them.

This is a module you will add to your bindings as follows:

XmlMapper mapper = new XmlMapper();
    
XmlSerializerProvider provider = new XmlSerializerProvider(new XmlRootNameLookup());
provider.setNullValueSerializer(new NullSerializer());
mapper.setSerializerProvider(provider);
mapper.registerModule(new NullPointerModule());

NullPointerModule implements its own customized serializer to pass a property needed for introspection of the current field.

NullPointerModule.java:

public class NullPointerModule extends SimpleModule implements java.io.Serializable {
    private static final long serialVersionUID = 1L;

    @Override
    public void setupModule(SetupContext context) {
        // Need to modify BeanDeserializer, BeanSerializer that are used
        context.addBeanSerializerModifier(new XmlBeanSerializerModifier() {
            @Override
            public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
                for (int i = 0, len = beanProperties.size(); i < len; ++i) {
                    BeanPropertyWriter bpw = beanProperties.get(i);
                    if (bpw.getClass().equals(BeanPropertyWriter.class)) {
                        beanProperties.set(i, new NullCheckedBeanPropertyWriter(bpw));
                    }
                }
                return beanProperties;
            }
        });
        super.setupModule(context);
    }

}

Next is the actual NullSerializer, this accepts the property writer and determines if the field does need the nil field or not.

NullSerializer.java:

public class NullSerializer extends JsonSerializer<Object> {

    @SuppressWarnings("unused")
    public void serializeWithProperty(BeanPropertyWriter propertyWriter, Object value, JsonGenerator jgen, SerializerProvider provider) {
        ToXmlGenerator xGen = (ToXmlGenerator) jgen;
        XmlElement annotation = null;

        if (propertyWriter != null) {
            AnnotatedMember member = propertyWriter.getMember();
            annotation = member.getAnnotation(XmlElement.class);
        }

        try {
            if (annotation != null) {
                if (annotation.nillable()) {
                    xGen.writeStartObject();
                    XMLStreamWriter staxWriter = xGen.getStaxWriter();

                    staxWriter.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
                    staxWriter.writeAttribute("xsi:nil", "true");
                    xGen.writeEndObject();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
        serializeWithProperty(null, value, jgen, provider);
    }
}

Lastly is the override for the propertyWriters. This is a bit of a hack since this can fail if the property writer itself was replaced by another class in another module.

NullCheckedBeanPropertyWriter.java:

public class NullCheckedBeanPropertyWriter extends BeanPropertyWriter {
    public NullCheckedBeanPropertyWriter(BeanPropertyWriter base) {
        super(base);
    }

    @Override
    public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
        final Object value = (_accessorMethod == null) ? _field.get(bean)
                : _accessorMethod.invoke(bean);

        // Null handling is bit different, check that first
        if (value == null) {
            if (_nullSerializer != null) {
                gen.writeFieldName(_name);
                if (_nullSerializer instanceof NullSerializer) {
                    NullSerializer nullSerializer = (NullSerializer) _nullSerializer;
                    nullSerializer.serializeWithProperty(this, bean, gen, prov);
                    return;
                }
                _nullSerializer.serialize(null, gen, prov);
            }
            return;
        }
        super.serializeAsField(bean, gen, prov);
    }
}

The fields can then be added with @XmlElement(nillable=true) to make them work to your needs.

like image 59
Rodney Pantonial Avatar answered Dec 01 '25 00:12

Rodney Pantonial



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!