My XSD
structure is like the below:-
I voted up gbvb's answer.
I don't understand why do you want this but.
The empty element with xmlns:xsi
and xsi:nil
is the right way to go.
Without those attributes any reasonable
parsers will give you the empty string even if the element is self-closed
.
Say you want to give clients an integer value which means the highest score in many players' scores.
When you can calculate you can give the right value.
When there is no player who actually scored yet, you should the right value as NULL
or nil
which means there is no record accumulated.
<highestScore among="128">98</highestScore>
Can be said the highest score is 98 of of 128 tries.
And
<highestScore among="0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:nil="true"/>
Can be said there is no highest score because there is no scores recorded.
But
<highestScore/>
Doesn't mean anything but a simple self-closed empty element.
NOTE #1: I am the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
NOTE #2: The output that you are seeing matches what you have mapped with JAXB. For more information see:
REPRESENTING NULL AS AN EMPTY ELEMENT
If you want to represent null as an empty element, there are a couple of options.
Option #1 - Using the Standard JAXB APIs
DateAdapter
You could use an XmlAdapter
to change the way an instance of Date
is marshalled to XML. We will convert the date to an instance of a class that has one property mapped with @XmlValue
(see http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html). The JAXB RI does not call the XmlAdapter
mechanism for null values, so you will need to use a JAXB impl that does such as MOXy.
package forum11743306;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.datatype.XMLGregorianCalendar;
public class DateAdapter extends XmlAdapter<DateAdapter.AdaptedDate, XMLGregorianCalendar>{
@Override
public AdaptedDate marshal(XMLGregorianCalendar date) throws Exception {
AdaptedDate adaptedDate = new AdaptedDate();
adaptedDate.value = date;
return adaptedDate;
}
@Override
public XMLGregorianCalendar unmarshal(AdaptedDate adaptedDate) throws Exception {
return adaptedDate.value;
}
public static class AdaptedDate {
@XmlValue
public XMLGregorianCalendar value;
}
}
Root
The XmlAdapter
is referenced using the @XmlJavaTypeAdapter
annotation.
package forum11743306;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.datatype.XMLGregorianCalendar;
@XmlRootElement
public class Root {
private XMLGregorianCalendar xyzDate;
@XmlElement(name = "XYZDate", required=true, nillable = true)
@XmlJavaTypeAdapter(DateAdapter.class)
public XMLGregorianCalendar getXyzDate() {
return xyzDate;
}
public void setXyzDate(XMLGregorianCalendar xyzDate) {
this.xyzDate = xyzDate;
}
}
Option #2 - Using MOXy's @XmlNullPolicy Extension
MOXy offers an @XmlNullPolicy
extension that gives you some flexibility in how you represent null.
package forum11743306;
import javax.xml.bind.annotation.*;
import javax.xml.datatype.XMLGregorianCalendar;
import org.eclipse.persistence.oxm.annotations.*;
@XmlRootElement
public class Root {
private XMLGregorianCalendar xyzDate;
@XmlElement(name = "XYZDate", required=true, nillable = true)
@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE)
public XMLGregorianCalendar getXyzDate() {
return xyzDate;
}
public void setXyzDate(XMLGregorianCalendar xyzDate) {
this.xyzDate = xyzDate;
}
}
Other Files
The following files can be used with either option to complete the example.
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties
in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum11743306;
import javax.xml.bind.*;
import javax.xml.datatype.DatatypeFactory;
import org.eclipse.persistence.Version;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
System.out.println(Version.getVersion());
System.out.println(jc.getClass());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Root root = new Root();
root.setXyzDate(null);
marshaller.marshal(root, System.out);
root.setXyzDate(DatatypeFactory.newInstance().newXMLGregorianCalendar("2012-08-01"));
marshaller.marshal(root, System.out);
}
}
Output
2.4.0
class org.eclipse.persistence.jaxb.JAXBContext
<?xml version="1.0" encoding="UTF-8"?>
<root>
<XYZDate/>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<XYZDate>2012-08-01</XYZDate>
</root>
Blaise's answer is good but it is outdated. There is a much better and simpler method to achieve the same. I have searched many forums and combined different solutions to get to this. I am sharing here so that it will be helpful for others.
Note: The below solution is more general case than just for date.
Session Event Adapter Class
Add the below class to a convenient package in your code.
package com.dev
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);
}
}
}
}
}
Entity Class
package com.dev;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
@XmlRootElement(name = "Entity")
public class Entity {
@XmlElement(name = "First_Name", required=true, nillable = true)
private String firstName;
@XmlElement(name = "Last_Name" , required=true, nillable = true)
private String lastName;
public Entity(){}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
DemoApp Class
package com.dev;
import javax.xml.bind.*;
import org.eclipse.persistence.*;
import java.util.Map;
import java.util.HashMap;
public class DemoApp {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String,Object>(1);
SessionEventListener sessionEventListener = new NullSessionEventListener();
properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener);
JAXBContext context = JAXBContextFactory.createContext(new Class[] {ListofEntities.class, list.get(0).getClass()},properties);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Entity entity = new Entity();
entity.setfirstName(null);
entity.setLastName(null);
marshaller.marshal(entity, System.out);
entity.setfirstName("Ramu");
entity.setLastName("K");
marshaller.marshal(entity, System.out);
}
}
Output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<First_Name/>
<Last_Name/>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<First_Name>Ramu</First_Name>
<Last_Name>Ramu</Last_Name>
</root>
Entity Class
package com.dev;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
@XmlRootElement(name = "Entity")
public class Entity {
@XmlElement(name = "First_Name", required=true, nillable = true)
@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE)
private String firstName;
@XmlElement(name = "Last_Name" , required=true, nillable = true)
private String lastName;
public Entity(){}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
DemoApp Class
package com.dev;
import javax.xml.bind.*;
import org.eclipse.persistence.*;
public class DemoApp {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContextFactory.createContext(new Class[] {ListofEntities.class, list.get(0).getClass()},null);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Entity entity = new Entity();
entity.setfirstName(null);
entity.setLastName(null);
marshaller.marshal(entity, System.out);
entity.setfirstName("Ramu");
entity.setLastName("K");
marshaller.marshal(entity, System.out);
}
}
Output:
In this output, we see only the element with XmlNullPolicy annotation is shown when value is null. The other element is omitted because of default behavior of jaxb.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<First_Name/>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<root>
<First_Name>Ramu</First_Name>
<Last_Name>Ramu</Last_Name>
</root>
References:
Where to include jaxb.properties file?
Represent null value as empty element in xml jaxb
You should read nillable and minOccurs XSD element attributes because the difference between nil
and an empty
element is significant in XML
. i.e. xsi:nil=true
is similar to SQL NULL
but having empty element represents the presents of an empty element. :)
I know it is confusing.
To fix your specific issue, if you are using JAXB
serialization to generate that, i recommend reading How to instantiate an empty element with JAXB. The question itself shows you how to generate an empty element.