Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Represent null value as empty element in xml jaxb

Tags:

java

xml

jaxb

moxy

I need to display null value as empty element in jaxb. I am using moxy implementation of jaxb. I found this option

@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE)

Is there any similar extension that can be applied at Class level (for all elements defined in it)

like image 482
Anand B Avatar asked Jun 28 '13 13:06

Anand B


People also ask

How do you represent null in XML?

Ways to represent a null value In an XML document, the usual way to represent a null value is to leave the element or attribute empty. Some business messages use a special value to represent a null value: <price>-999</price> .

What is @XmlRootElement?

When a top level class or an enum type is annotated with the @XmlRootElement annotation, then its value is represented as XML element in an XML document. This annotation can be used with the following annotations: XmlType , XmlEnum , XmlAccessorType , XmlAccessorOrder .

Do not include null values in JSON response?

You can ignore null fields at the class level by using @JsonInclude(Include. NON_NULL) to only include non-null fields, thus excluding any attribute whose value is null. You can also use the same annotation at the field level to instruct Jackson to ignore that field while converting Java object to json if it's null.

What is the use of @XmlElement?

A JavaBean property, when annotated with @XmlElement annotation is mapped to a local element in the XML Schema complex type to which the containing class is mapped.


1 Answers

I would strongly recommend representing null with either the absence of the node or with the xsi:nil="true" attribute. This works best with schema validation (i.e. <age/> or <age></age> is not a valid element of type xsd:int. However if you can't here is how you can accomplish your use case:

STANDARD JAXB BEHAVIOUR

Using the standard APIs you can control whether null is represented as an absent node or with xsi:nil="true" with the @XmlElement annotation (see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {

    private String street;

    @XmlElement(nillable=true)
    private String city;

}

Below is the XML output if the values of both fields are null.

<?xml version="1.0" encoding="UTF-8"?>
<address>
   <city xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</address>

MOXy - OVERRIDING THIS BEHAVIOUR PER CLASS

MOXy does not provide an annotation to specify the null policy for all the properties on a class. However you can leverage a DescriptorCustomizer via the @XmlCustomizer annotation and tweak the native MOXy mapping metadata to accomplish the same thing.

DescriptorCustomizer (AddressCustomizer)

import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;

public class AddressCustomizer implements DescriptorCustomizer {

    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        for(DatabaseMapping mapping : descriptor.getMappings()) {
            if(mapping.isAbstractDirectMapping()) {
                XMLDirectMapping xmlDirectMapping = (XMLDirectMapping) mapping;
                xmlDirectMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.EMPTY_NODE);
                xmlDirectMapping.getNullPolicy().setNullRepresentedByEmptyNode(true);
            }
        }
    }

}

DomainModel (Address)

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCustomizer;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(AddressCustomizer.class)
public class Address {

    private String street;

    @XmlElement(nillable=true)
    private String city;

}

Output

<?xml version="1.0" encoding="UTF-8"?>
<address>
   <street/>
   <city/>
</address>

MOXy - OVERRIDING THIS BEHAVIOUR FOR ALL CLASSES

If instead you want to override null handling for all of the mapped classes I would recommend using a SessionEventListener instead. If you prefer you could also use this approach to update the metadata for a single class.

SessionEventListener (NullPolicySessionEventListener)

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.eclipse.persistence.sessions.*;

public class NullPolicySessionEventListener extends SessionEventAdapter {

    @Override
    public void preLogin(SessionEvent event) {
        Project project = event.getSession().getProject();
        for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
            for(DatabaseMapping mapping : descriptor.getMappings()) {
                if(mapping.isAbstractDirectMapping()) {
                    XMLDirectMapping xmlDirectMapping = (XMLDirectMapping) mapping;
                    xmlDirectMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.EMPTY_NODE);
                    xmlDirectMapping.getNullPolicy().setNullRepresentedByEmptyNode(true);
                }
            }
        }
     }

}

Demo Code

import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.sessions.SessionEventListener;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);
        SessionEventListener sessionEventListener = new NullPolicySessionEventListener();
        properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener);
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Address.class}, properties);

        Address address = new Address();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(address, System.out);
    }

}

Output

<?xml version="1.0" encoding="UTF-8"?>
<address>
   <street/>
   <city/>
</address>
like image 126
bdoughan Avatar answered Oct 23 '22 02:10

bdoughan