I have an object tree of POJOs that represents an XML Schema. This was created with the following jaxb
ant script.
I want to validate the root POJO and its children entities against the schema for missing attributes.
My code is the following: (try/catch block omitted, inspired by SO question How to validate against schema in JAXB 2.0 without marshalling?)
public boolean validateAgainstSchema(Pojo pojo)
{
JAXBContext jc;
jc = JAXBContext.newInstance(Pojo.class);
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new ClassPathResource("schema.xsd").getFile());
Marshaller marshaller = jc.createMarshaller();
marshaller.setSchema(schema);
marshaller.marshal(schema, new DefaultHandler());
return true;
}
One of my attributes (pojo.childEntity.someAttribute
) is a date
XSD
<xsd:attribute name="some_date" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:date" />
</xsd:simpleType>
</xsd:attribute>
Java
@XmlAttribute(name = "someDate", required = true)
protected XMLGregorianCalendar someDate;
It gets populate from a java.util.Date
object from another POJO (one that is mapped with Hibernate).
private static final XMLGregorianCalendar dateToCalendar(Date date)
{
if (date == null)
return null;
try
{
GregorianCalendar c = new GregorianCalendar();
c.setTime(date);
return DatatypeFactory.newInstance()
.newXMLGregorianCalendar(c);
}
catch (DatatypeConfigurationException e)
{
e.printStackTrace();
return null;
}
}
The exception is:
javax.xml.bind.MarshalException
- with linked exception:
[org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: '2001-05-11T00:00:00.000+02:00' is not a valid value for 'date'.]
This looks like JAXB tries to set both date and time for a field that must carry only the date, and XMLGregorianCalendard is simply a datetime container.
The question is: what causes the error? How to fix?
By default the output for an XMLGregorianCalendar
property will be based on how you create it. If you populate the time portion, then the time portion will be output to XML. You can call the getXMLSchemaType()
method to see what the corresponding XML representation is:
You can use the @XmlSchemaType
annotation to override the representation.
Java Model (Root)
Below is an object with 3 XMLGregorianCalendar
fields. On the 3rd I will use an @XmlSchemaType
annotation to specify the schema type.
import javax.xml.bind.annotation.*;
import javax.xml.datatype.XMLGregorianCalendar;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
protected XMLGregorianCalendar default1;
protected XMLGregorianCalendar default2;
@XmlSchemaType(name="date")
protected XMLGregorianCalendar schemaTypeDate;
}
Demo Code
In the demo code below we will create 2 instances of XMLGregorianCalendar
. One will have schema type date
the other dateTime
. By default this is the XML representation used when marshalling to XML. On the schemaTypeDate
field we will use the @XmlSchemaType
annotation to override the default.
import javax.xml.bind.*;
import javax.xml.datatype.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
DatatypeFactory df = DatatypeFactory.newInstance();
XMLGregorianCalendar date = df.newXMLGregorianCalendar("2013-07-03");
XMLGregorianCalendar dateTime = df.newXMLGregorianCalendar("1999-12-31T23:59:00");
Root root = new Root();
root.default1 = date;
root.default2 = dateTime;
root.schemaTypeDate = dateTime;
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<default1>2013-07-03</default1>
<default2>1999-12-31T23:59:00</default2>
<schemaTypeDate>1999-12-31</schemaTypeDate>
</root>
UPDATE
Ok, since I have loooooooooooooooooooooooooooooooooots of XmlGregorianCalendars is there a way to tell XJC to add the xmlSchemaType attribute to all XGCs?
JAXB will do this for you when the schema type is one of the build in XML Schema types i.e. xsd:date
or xsd:dateTime
, but not when you have extended one of these types.
XML Schema (schema.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="root">
<sequence>
<element name="dateElement" type="date" />
<element name="dateTimeElement" type="dateTime" />
<element name="extendedDateElement">
<simpleType>
<restriction base="date" />
</simpleType>
</element>
</sequence>
<attribute name="dateAttribute" type="date" />
<attribute name="dateTimeAttribute" type="dateTime" />
<attribute name="extendedDateAttribute">
<simpleType>
<restriction base="date" />
</simpleType>
</attribute>
</complexType>
</schema>
Root
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "root", propOrder = {
"dateElement",
"dateTimeElement",
"extendedDateElement"
})
public class Root {
@XmlElement(required = true)
@XmlSchemaType(name = "date")
protected XMLGregorianCalendar dateElement;
@XmlElement(required = true)
@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar dateTimeElement;
@XmlElement(required = true)
protected XMLGregorianCalendar extendedDateElement;
@XmlAttribute(name = "dateAttribute")
@XmlSchemaType(name = "date")
protected XMLGregorianCalendar dateAttribute;
@XmlAttribute(name = "dateTimeAttribute")
@XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar dateTimeAttribute;
@XmlAttribute(name = "extendedDateAttribute")
protected XMLGregorianCalendar extendedDateAttribute;
}
If the date is '2001-05-11T00:00:00.000+02:00' use
<xsd:restriction base="xsd:dateTime" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With