JAXB Bindings File Sets @XmlElement type to String instead of XMLGregorianCalendar
UPDATE
summarizing:
- you have a schema that uses date style somewhere, and you cannot change the schema
- you have some XML data that uses that schema and specify some date with timezone (so it's
yyyy-MM-ddXXX
format) - you want to remove the
XXX
part from the representation of the date in that file (date itself does not ship any timezone, date is just a number)
so this could be a sample schema:
<?xml version="1.0" encoding="UTF-8"?><schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <element name="foo"> <complexType> <sequence> <element name="bar" type="date" minOccurs="1" maxOccurs="1"/> </sequence> </complexType> </element></schema>
this could be a sample data:
<?xml version="1.0" encoding="UTF-8"?><foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="foo.xsd"> <bar>2014-01-01+06:00</bar></foo>
this is JAXB annotated class
@XmlRootElement@XmlAccessorType(XmlAccessType.FIELD)public class Foo implements Serializable{ private static final long serialVersionUID = 1L; @XmlElement(name = "bar") @XmlJavaTypeAdapter(DateAdapter.class) @XmlSchemaType(name = "date") private Date bar; // getters/setters}
this is date adapter
public class DateAdapter extends XmlAdapter<String, Date>{ @Override public String marshal(Date date) { DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); df.setTimeZone(TimeZone.getTimeZone("GMT")); return df.format(date); } @Override public Date unmarshal(String date) throws ParseException { DateFormat df = new SimpleDateFormat("yyyy-MM-ddXXX"); return df.parse(date); }}
this is the main, validating against the schema:
public static void main(String[] args) throws JAXBException, SAXException{ JAXBContext context = JAXBContext.newInstance(Foo.class); SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = sf.newSchema(Foo.class.getResource("/foo.xsd")); Unmarshaller unmarshaller = context.createUnmarshaller(); unmarshaller.setSchema(schema); Foo foo = (Foo) unmarshaller.unmarshal(Foo.class.getResource("/foo.xml")); System.out.println("unmarshalled: " + foo.getBar()); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "foo.xsd"); marshaller.setSchema(schema); marshaller.marshal(foo, System.out);}
and this is the output, timezone has been removed and date representation has obviously changed
unmarshalled: Tue Dec 31 19:00:00 CET 2013<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="foo.xsd"> <bar>2013-12-31</bar></foo>
maybe this date representation change is not what you'd expect, but this is not a JAXB concern, the date represented has not changed.
i was forgetting the bindings to reverse generate Foo:
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc" jaxb:version="2.0"> <jaxb:globalBindings> <xjc:javaType name="java.util.Date" xmlType="xsd:date" adapter="aaa.DateAdapter" /> </jaxb:globalBindings></jaxb:bindings>
END OF UPDATE
sorry, too long for a comment...
i can't understand:
- why the hell are you using
XmlGregorianCalendar
? - why should you
marshal
/unmarshal
(serialize
/deserialize
) to the very same data structure? - why should you remove timezone??
and
- i use straight and simple
java.util.Date
marshal
/unmarshal
should always involveString
s (at least for XML)- i really don' see a good reason to arbitrarily remove a piece of a date representation. maybe you want to serialize it in an absolute way.
however
public class DateAdapter extends XmlAdapter<String, Date>{ @Override public String marshal(Date date) { DateFormat df = DateFormat.getDateTimeInstance(); df.setTimeZone(TimeZone.getTimeZone("GMT")); return df.format(date); } @Override public Date unmarshal(String date) throws ParseException { DateFormat df = DateFormat.getDateTimeInstance(); df.setTimeZone(TimeZone.getTimeZone("GMT")); return df.parse(date); } public static void main(String[] args) throws ParseException { DateAdapter adapter = new DateAdapter(); String str = adapter.marshal(new Date()); System.out.println(str); // 16-dic-2013 10.02.09 --> to gmt Date date = adapter.unmarshal(str); System.out.println(date); // Mon Dec 16 11:02:09 CET 2013 --> correct, i'm gmt+1 }}
You have to specify the java type differently, in your case:
<jxb:javaType name="javax.xml.datatype.XMLGregorianCalendar" xmlType="xs:date" printMethod="foo.bar.TimezoneRemoverAdapter.marshall" parseMethod="foo.bar.TimezoneRemoverAdapter.unmarshall" />
It works fine for me and I did something similar with more adapters:
<jaxb:globalBindings> <xjc:serializable uid="12343" /> <jaxb:javaType name="java.util.Date" xmlType="xs:date" printMethod="com.foo.DateAdapter.printDate" parseMethod="com.foo.DateAdapter.parseDate" /> <jaxb:javaType name="java.util.Date" xmlType="xs:dateTime" printMethod="com.foo.DateAdapter.printDate" parseMethod="com.foo.DateAdapter.parseDate" /> <jaxb:javaType name="java.util.Date" xmlType="xs:time" printMethod="com.foo.TimeAdapter.printTime" parseMethod="com.foo.TimeAdapter.parseTime" /></jaxb:globalBindings>
I put the above bindings as a globalBindings
in a different file with .xjb
extension and I use it everywhere I need it.