Main problem
Empty String
not a valid value for type xsd:date
. To be valid in an XML schema, an optional element must be represented as a missing node.,
Why the main problem affects you
All JAXB implementations recognize that an empty String
not a valid value for xsd:date
. They do this by reporting this to the ValidationEventHandler
instance. You yourself see this by following these steps:
Unmarshaller unmarshaller = jc.createUnmarshaller(); unmarshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { System.out.println(event); return true; } });
The JAX-WS implementation that you use uses EclipseLink MOXy as the JAXB provider. And in the version that you use MOXy, it throws an exception by default if instead of FATAL_ERROR
ValidationEvent
meets the severity ERROR
, as a reference implementation. Since then, this has been fixed in the following error:
Work around
If you use the JAXB API directly, you can simply override the default ValidationEventHandler
. In a JAX-WS environment, a XmlAdapter
can be used to provide custom conversion logic. We will use the XmlAdapter
to override how conversion to / from Date
handled.
XmlAdapter (DateAdapter)
import java.text.SimpleDateFormat; import java.util.Date; import javax.xml.bind.annotation.adapters.XmlAdapter; public class DateAdapter extends XmlAdapter<String, Date>{ private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); @Override public Date unmarshal(String v) throws Exception { if(v.length() == 0) { return null; } return dateFormat.parse(v); } @Override public String marshal(Date v) throws Exception { if(null == v) { return null; } return dateFormat.format(v); } }
Java Model (Root)
The XmlAdapter
link XmlAdapter
used using the @XmlJavaTypeAdapter
annotation. If you want this XmlAdapter
applied to all instances of Date
, you can register it at the package level (see http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html ).
import java.util.Date; import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { @XmlSchemaType(name = "date") @XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class) private Date abc; @XmlSchemaType(name="date") @XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class) private Date qwe; }
Demo code
Below is a separate example that you can run to see that everything works.
jaxb.properties
In the standalone example of using MOXy as your JAXB provider, you need to include a file named jaxb.propeties
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
Input.xml
<?xml version="1.0" encoding="UTF-8"?> <root> <abc></abc> <qwe>2013-09-05</qwe> </root>
Demo
import java.io.File; import javax.xml.bind.*; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Root.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); File xml = new File("src/forum18617998/input.xml"); Root root = (Root) unmarshaller.unmarshal(xml); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, System.out); } }
Exit
Note that in the marshalled XML, the Date
field, which was null, was allocated as the missing element (see http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html ) .
<?xml version="1.0" encoding="UTF-8"?> <root> <qwe>2013-09-05</qwe> </root>