Reputation: 331
I am having trouble to marshall/unmarshall generic type using JAXB. Everything works fine, but when my generic field is Date (java.util.Date), unmarshalling will produce XMLGregorianCalendar instead
here is a piece of code I am talking about:
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Entity<T> {
private T field;
public Entity() {
}
public T getField() {
return field;
}
public void setField(T field) {
this.field = field;
}
}
import java.io.*;
import java.util.Date;
import javax.xml.bind.*;
public class JaxbTest {
public JaxbTest() {
Entity<Date> ent = new Entity<Date>();
ent.setField(new Date());
StringWriter sw = new StringWriter();
try {
// marshall
JAXBContext jaxbContext = JAXBContext.newInstance(Entity.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(ent, sw);
// unmarshall
JAXBContext jc = JAXBContext.newInstance(Entity.class);
Unmarshaller u = jc.createUnmarshaller();
Entity<Date> ent2 = (Entity<Date>) u.unmarshal(new StringReader(sw
.toString()));// exception is thrown here
System.out.println(ent2.getField());
} catch (JAXBException e11) {
e11.printStackTrace();
}
}
public static void main(String[] args) {
new JaxbTest();
}
}
the above code throws exception:
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl cannot be cast to java.util.Date
at JaxbTest.<init>(JaxbTest.java:31)
at JaxbTest.main(JaxbTest.java:38)
Could you help me how to fix it? I was thinking about some adapter that would be "active" only if field is Date, but I don't think it is possible
thanks
Upvotes: 3
Views: 2617
Reputation: 66
I had a similar problem with a generic field in an abstract class. I solved it by adding an annotated getter/setter which parses the value to/from a string. The getter/setter is then overwritten in my subclasses.
This was my solution:
@XmlSeeAlso({DateEntity.class})
@XmlType
public abstract class Entity<T> {
private T field;
public Entity() {}
public T getField() {
return this.field;
}
public void setField(T field) {
this.field = field;
}
@XmlAttribute(name="value", required = true)
public abstract String getSerializeField();
public abstract void setSerializeField(String value);
}
My date sublcass:
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name="dateEntity")
public class DateEntity extends Entity<Date> {
public DateEntity() {}
@Override
public String getSerializeField(){
DateFormat formatter = DateFormat.getInstance();
return formatter.format(field);
}
@Override
public void setSerializeField(String value) {
DateFormat formatter = DateFormat.getInstance();
this.value = formatter.parse(value);
}
}
Hope this is helpful...
Upvotes: 0
Reputation: 149047
A JAXBContext
is built on classes and not types. As far your JAXB (JSR-222) implementation is aware, the field
property is of type Object
. If you add the following to your code:
String xml = sw.toString();
System.out.println(xml);
You will see the output XML is the following. An xsi:type
attribute has been added to preserve that the text value contains date information. On the unmarshal
operation since JAXB thinks the property is of type Object
it will recognize the xsi:type
attribute and convert the value to the default date type which is XMLGregorianCalendar
.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entity>
<field xsi:type="xs:dateTime" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2012-10-29T15:20:17.763-04:00</field>
</entity>
Upvotes: 1