考虑以下抽象类 –
public abstract class Car { public abstract void drive(double miles); }
这是扩展上述类的示例类(用于说明目的).
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 } }
我有一个包含Car对象的报表类 –
@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; } }
这里是使用JAXB来传递CarTestReport对象的代码片段 –
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); }
问题是,由于Car的抽象类型,JAXB忽略它并且在它对CarTestReport对象进行编组时不会对法拉利对象进行编组.我得到的输出是 –
<carTestReport> <car/> <date>July 6 2012</date> <miles>200.0</miles> </carTestReport>
如您所见,即使填充了法拉利对象,也没有在“汽车”节点下输入任何内容.如何解决这个问题呢?
对于任何可能的JAXB注释类,JAXB系统都不会查看类路径.你必须帮助它找到它们.在您的示例代码中,它根本不知道法拉利类的存在. (它只能看到Car,因为这是CarTestReport中getter的返回类型.)
告诉JAXB关于法拉利的一种快速而肮脏的方法是在Car类的顶部添加@XmlSeeAlso({Ferrari.class}).然后你会得到这样的输出:
<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>
告诉JAXB关于法拉利的另一种方法是将该类传递给JAXBContext.newInstance方法,即:
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class, Ferrari.class);
或者,如果所有JAXB类都在同一个包中,例如com.mycompany.carstuff,然后你可以这样做:
JAXBContext jaxbContext = JAXBContext.newInstance("com.mycompany.carstuff");
在最后一种情况下,它将搜索该包中的所有类.
如果你想让它发出一个名为ferrari的元素(而不是像上面那样的<car xsi:type =“ferrari”>),一种可能性是将@XmlType添加到你的Car类的顶部,如下所示:
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); }
…并将@XmlRootElement放在法拉利上,例如:
import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class Ferrari extends Car { // ... }
根据我的理解,这种注释组合告诉JAXB Car类映射到XML模式类型(因此你不会得到任何名为“car”的元素),并且法拉利类是该类型的元素(所以你可以有名为“ferrari”的元素. @XmlRootElement中的“根”是误导性的…它可以是对象结构中的任何元素.
翻译自:https://stackoverflow.com/questions/11368076/how-to-make-jaxb-marshaller-marshal-abstract-class-based-on-its-runtime-type