Reputation: 43
I wonder if someone can help me with a JAXB problem.
If I have an abstract class with 2 concrete implementations: For example (I have left out most of the markup/xml for brevity):
public abstract class Vehicle{}
public class Car extends Vehicle{}
public class Van extends Vehicle{}
Is there a way to have the xml below unmarshall correctly to the appropriate concrete class
<request>
<car>...</car>
</request>
rather than the following:
<request>
<vehicle xsi:type="car"></vehicle>
</request>
The reason I need this is to be backward compatible with our already published API.
Thanks in advance.
Upvotes: 0
Views: 863
Reputation: 1001
I have just answered in russian speaking community on similar question. Probably you looking for something like that:
@XmlElements({
@XmlElement(name = "car", type = Car.class),
@XmlElement(name = "van", type = Van.class)
})
public List<Vehicle> getVehicles() {
return vehicles;
}
Some quick example:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.io.StringReader;
import java.util.List;
public class Test {
public static void main(String... args) throws JAXBException {
String xmldata = "<request><car></car><van></van></request>";
StringReader reader = new StringReader(xmldata);
JAXBContext jaxbContext = JAXBContext.newInstance(Request.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Request request = (Request) unmarshaller.unmarshal(reader);
for (Vehicle object : request.getVehicles()) {
System.out.println(object.getClass());
}
}
}
@XmlRootElement(name = "request")
class Request {
private List<Vehicle> vehicles;
@XmlElements({
@XmlElement(name = "car", type = Car.class),
@XmlElement(name = "van", type = Van.class)
})
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
}
abstract class Vehicle {
}
class Van extends Vehicle {
}
class Car extends Vehicle {
}
The output will be:
class Car
class Van
UPD:
Update after comment. For single entry it will work anyway just remove List
:
@XmlRootElement(name = "request")
class Request {
private Vehicle vehicles;
@XmlElements({
@XmlElement(name = "car", type = Car.class),
@XmlElement(name = "van", type = Van.class)
})
public Vehicle getVehicles() {
return vehicles;
}
public void setVehicles(Vehicle vehicles) {
this.vehicles = vehicles;
}
}
Hope this will help.
Upvotes: 3
Reputation: 9492
You can use annotations and annotate the concrete implementations. In this case @XmlType() above Car or Van. This way you will keep your xml generic.
Upvotes: 1