Consider the following abstract class -
public abstract class Car
{
public abstract void drive(double miles);
}
Here's a sample class (for illustration purposes) that extends the above class.
public class Ferrari extends Car
{
private String lastUsed; // Ferrari specific field not in Car
private boolean f1Car; // Ferrari specific field not in Car
@XmlElement
public void setF1Car(boolean f1Car)
{
this.f1Car = f1Car;
}
public boolean isF1Car() { return f1Car; }
@XmlElement
public void setLastUsed(String lastUsed)
{
this.lastUsed = lastUsed;
}
public String getLastUsed() { return lastUsed; }
public void drive(double miles)
{
// implementation
}
}
I have a report class that contains a Car object -
@XmlRootElement
public class CarTestReport
{
private String date;
private double miles;
private Car car;
@XmlElement
public void setDate(String date) { this.date = date;}
public String getDate() {return date;}
@XmlElement
public void setMiles(double miles) { this.miles = miles; }
public double getMiles() {return miles;}
@XmlElement
public void setCar(Car car) { this.car = car; }
public Car getCar() { return car; }
}
And here is the piece of code using JAXB to Marshall a CarTestReport object -
public static void main(String[] args) throws Exception
{
Ferrari ferrari = new Ferrari();
ferrari.setLastUsed("July 5 2012");
ferrari.setF1Car(false);
CarTestReport report = new CarTestReport();
report.setDate("July 6 2012");
report.setMiles(200);
report.setCar(ferrari);
File file = new File("carTestReport.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(report, file);
}
The problem is, because of the abstract type Car, JAXB ignores it and doesn't marshall the Ferrari object when it marshals the CarTestReport object. The output I get is this -
<carTestReport>
<car/>
<date>July 6 2012</date>
<miles>200.0</miles>
</carTestReport>
As you can see, nothing was entered under the 'car' node, even though the Ferrari object was populated. How to solve this problem?
The JAXB system doesn't look through the classpath for any possible JAXB-annotated classes. You have to help it find them. In your sample code, it simply doesn't know about the existence of the Ferrari
class. (It only sees Car
because that's the return type of the getter in CarTestReport
.)
One quick and dirty way to tell JAXB about Ferrari
is to add @XmlSeeAlso({Ferrari.class})
at the top of your Car
class. Then you'll get output like this:
<carTestReport>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ferrari">
<f1Car>false</f1Car>
<lastUsed>July 5 2012</lastUsed>
</car>
<date>July 6 2012</date>
<miles>200.0</miles>
</carTestReport>
Another way to tell JAXB about Ferrari
would be to pass that class to the JAXBContext.newInstance
method, i.e.:
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class,
Ferrari.class);
Or if all of your JAXB classes are in the same package, e.g. com.mycompany.carstuff
, then you could do this:
JAXBContext jaxbContext = JAXBContext.newInstance("com.mycompany.carstuff");
And in this last case it WILL search for all classes in that package.
If you want it to emit an element named ferrari
(instead of the <car xsi:type="ferrari">
like above), one possibility is to add @XmlType
to the top of your Car
class, like this:
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
@XmlSeeAlso({Ferrari.class})
@XmlType
public abstract class Car {
public abstract void drive(double miles);
}
...and put @XmlRootElement
on Ferrari
, e.g.:
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Ferrari extends Car {
// ...
}
From what I understand, this combination of annotations tells JAXB that the Car
class maps to an XML schema type (so you won't get any elements named "car"), and that the Ferrari
class is an element of that type (so you can have elements named "ferrari"). And the "root" in @XmlRootElement
is misleading... it can be an element anywhere in the structure of your objects.
You need to annotate your Ferrari class with @XmlRootElement
and replace @XmlElement
on the car attribute with @XmlElementRef
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