11

My XSD structure is like the below:-

<element name="XYZDate" maxOccurs="1" minOccurs="1" nillable="true" type="date"/>

When I set the null value in this field it allow me but at he time of XML Generation from JAXB marshaling it produce the output with

<XYZDate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>

Whereas in the result I want the out put as <XYZDate/> i.e. do not want the schema and other attribute.
I get rid of this with the help of using XMLStreamWriter but it produce the complete XML in Single line. I need formatted well formed XML. If I need to use IndentingXMLStreamWriter my Java version do not support it and I do not have control over Java Container to change or modify.

Please suggest any solution to form XML Well Formatted.

S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
Amit Telang
  • 131
  • 1
  • 3
  • 7
  • 1
    http://stackoverflow.com/questions/4616383/xmlstreamwriter-indentation – Jean Logeart Jul 31 '12 at 15:15
  • As I wrote I can not use IndentingXMLStreamWriter because I am using Eclips and do use this class I need to add JAXB-API 2.1.9 in the Lib which creates problem in Java 1.6 as it uses it own library. – Amit Telang Jul 31 '12 at 15:20
  • I changed the title of your question because JAXB is generating the correct output based on your metadata (see http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html). You are really asking for a way to represent null as a empty element. – bdoughan Jul 31 '12 at 16:25

4 Answers4

16

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>
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Hi Blaise,I am using Eclips Eurepa.Do I need to download something as while executing Demo class its giving me error linked exception: [java.lang.ClassNotFoundException: org.eclipse.persistence.jaxb.JAXBContextFactory]. – Amit Telang Aug 01 '12 at 09:07
  • @AmitTelang - You can download EclipseLink here: http://www.eclipse.org/eclipselink/downloads/ – bdoughan Aug 01 '12 at 10:05
  • 1
    @AmitTelang - I'm happy to help. You can use Eclipse Europa, but the Java EE version of Eclipse Juno has some interesting JAXB tooling and is probably worth trying out the new release. – bdoughan Aug 01 '12 at 12:35
  • Hi Blaise, Many Thanks. This demo class is working fine for me.But when I included this jaxb.properties in my application the generated XML is creating with ns0: appending in each and every XML tag. Why its including ns0: before every tag. – Amit Telang Aug 01 '12 at 12:41
  • @AmitTelang - Have you modified the demo at all? Can you start an email thread with me through my contact page (http://blog.bdoughan.com/p/contact_01.html). I can help you out further that way. – bdoughan Aug 01 '12 at 12:53
  • Hi Blaise, I emailed you. please look into this. – Amit Telang Aug 01 '12 at 13:23
  • Hi Blaise, I emailed you. please look into this. I checked and in demo class also I am not getting XYDDate as . Result that I am getting from Demo class is , so basically its removing XYZDate from the date instead of showing blank tag. – Amit Telang Aug 01 '12 at 13:41
  • Result of the Demo class: there is no tag when setting null value. 2012-08-01T19:23:39.427 – Amit Telang Aug 01 '12 at 13:54
  • @AmitTelang - From our email discussion the problem appears to be from you using an old version of EclipseLink (1.0 from Sept 2008). The current release is EclipseLink 2.4.0 (from June 2012). – bdoughan Aug 01 '12 at 16:11
  • Hi blaise We have mailed you an sample application,as per this application the generated xml file coming along with a additional Tag named ns0 when we have used eclipselink.jar version 2.4.0. Kindly go through it and advice us on this as we do not want that additional Tag. – Amit Telang Sep 17 '12 at 11:34
4

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.

Community
  • 1
  • 1
gbvb
  • 866
  • 5
  • 10
2

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.


Method - 1 : If you want to replace all null values with empty string in xml

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>

Method - 2 : If you want to replace only selected null values with empty string in xml

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:

  1. Where to include jaxb.properties file?

  2. Represent null value as empty element in xml jaxb

mkkr1009
  • 91
  • 3
0

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.

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • Actually I am creating XML for third party and there model do no understand xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" it simply understand . I need to go with the third party model hence problem was mentioned. – Amit Telang Aug 06 '12 at 12:18
  • I understand. I hope the 3rd party just doesn't understand `xmlns:xsi`, not throwing an exception. It's just an attribute. – Jin Kwon Aug 07 '12 at 01:00