我试图使用JAXB将XML编组到类heiarchy中 - 我希望heiarchy以通用的方式使用继承。我会更好地解释一下:
我有以下XML:
<Country name="USA">
<City name="NewYork">
<Street name="Something"/>
<Street name="Something2"/>
</City>
<City name="LosAngeles">
<Street name="Something"/>
<Street name="Something2"/>
</City>
<Country>
<Country .....>
<.....>
<.....
</Country>
等。其中eatch国家可以有多个城市,每个城市可以有多条街道。
我想创建一个名为GeneralLocation的类,它看起来像这样:
@XmlTransient
public abstract class GeneralLocation {
private String name;
protected List<GeneralLocation> sons;
@XmlAttribute(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<GeneralLocation> getSons(){
return this.sons
}
public void setSons(List<GeneralLocation> sons){
this.sons = sons;
}
}
然后创建Country,City和Street类以从GeneralLocation继承,并使用正确的JAXB解析名称覆盖getSons方法。
public class Country extends GeneralLocation{
@XmlElement(name="City")
public List<GeneralLocation> getSons(){
return this.sons
}
public void setSons(List<GeneralLocation> sons){
this.sons = sons;
}
}
我已经尝试过这个基本代码的各种变体,但没有一个能够完成这项工作。我没有添加任何特定的一个,因为他们都抛出了似乎表明我真的不在正确的道路上的奇怪的例外,所以我决定添加这个基本的骨架并希望你们的任何指针......
任何人都可以帮我吗? 感谢
答案 0 :(得分:5)
您可以使用@XmlElementRef
注释作为用例。
<强> GeneralLocation 强>
您可以在@XmlElementRef
媒体资源上使用sons
注释。 XML中元素的名称将基于与引用的对象的子类关联的根元素。
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlSeeAlso({Country.class, City.class, Street.class})
public abstract class GeneralLocation {
private String name;
protected List<GeneralLocation> sons;
@XmlAttribute(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlElementRef
public List<GeneralLocation> getSons(){
return this.sons;
}
public void setSons(List<GeneralLocation> sons){
this.sons = sons;
}
}
<强>国家强>
子类不需要覆盖sons
属性。他们唯一需要的是@XmlRootElement
注释,@XmlElementRef
注释将GeneralLocation
用于导出元素名称。
import javax.xml.bind.annotation.*;
@XmlRootElement(name="Country")
public class Country extends GeneralLocation {
}
<强>城市强>
import javax.xml.bind.annotation.*;
@XmlRootElement(name="City")
public class City extends GeneralLocation {
}
街景强>
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="Street")
public class Street extends GeneralLocation {
}
下面是一些示例代码,它将XML解组为对象,然后再将其封送回XML。请注意,由于我们在@XmlSeeAlso
类上使用了GeneralLocation
注释,因此在引导JAXBContext
时我们不需要指定所有子类。
<强>演示强>
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(GeneralLocation.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
GeneralLocation result = (GeneralLocation) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(result, System.out);
}
}
<强> input.xml中/输出强>
您问题中的XML无效,因为您没有一个根元素,所以对于我的回答,我选择只使用一个国家。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Country name="USA">
<City name="NewYork">
<Street name="Something"/>
<Street name="Something2"/>
</City>
<City name="LosAngeles">
<Street name="Something"/>
<Street name="Something2"/>
</City>
</Country>
答案 1 :(得分:1)
我的解决方案并不像您希望的那样通用,但我认为它可能有助于您理解为什么您收到了各种异常(我自己偶然发现许多人试图找到解决方案)。
首先,我使用以下XML作为测试(我假设你省略了root元素):
<root>
<Country name="USA">
<City name="NewYork">
<Street name="Something" />
<Street name="Something2" />
</City>
<City name="LosAngeles">
<Street name="Something" />
<Street name="Something2" />
</City>
</Country>
<Country name="France">
<City name="Paris">
<Street name="Champs-Elysees" />
<Street name="La sante" />
</City>
</Country>
</root>
至于课程本身,我保持与你的相同的结构,Country,City和Street继承自GeneralLocation。但是,我从GeneralLocation中删除了子项列表,而每个类(Street除外)都拥有一个具有正确子类型的List(例如,Country in Country)。 它似乎最符合您的目标,因为您的原始结构允许Street将City或Country作为子项,除了自然矛盾之外,如果您想从类模型编组为XML,则可能会导致问题。因此,我结束了以下课程:
public abstract class GeneralLocation {
protected String name;
public GeneralLocation() {
name = "";
}
@XmlAttribute(name = "name")
public String getName() {
return name;
}
}
我摆脱了@XmlTransient注释,因为根据我的经验,这不是必需的。
@XmlType
public class Street extends GeneralLocation {
public Street() {
}
}
街道没有孩子,因此无法将城市或国家作为儿童。我也刚刚意识到你没有在类定义之上的@XmlType注释,这可能是解析错误的原因。@XmlType
public class City extends GeneralLocation {
private List<Street> streets;
public City() {
streets = new LinkedList<Street>();
}
@XmlElement(name = "Street")
public List<Street> getStreets() {
return streets;
}
}
@XmlType
public class Country extends GeneralLocation{
private List<City> cities;
public Country() {
cities = new LinkedList<City>();
}
@XmlElement(name="City")
public List<City> getCities() {
return cities;
}
}
@XmlRootElement(namespace = "", name = "root")
public class Root {
private List<Country> countries;
public Root() {
countries = new LinkedList<Country>();
}
@XmlElement(name = "Country")
public List<Country> getCountries() {
return countries;
}
}
城市和国家在这里非常相似,他们都拥有各自孩子的列表(用户不能搞砸=安全性),JAXB可以通过注释访问超类中的name属性(但这取决于你的访问政策,见下文)。最后,我放了一个用@XmlRootElement标记的根类,并保存了这些国家。
最后,我建议您在包定义中定义访问者类型(文件package-info.java),以确保JAXB访问数据的方式是一致的(例如,只有注释):
@XmlAccessorType(XmlAccessType.NONE) //annotations only
package test;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
有了这个你应该全部解开你的xml :)作为奖励,我使用的主要:
public static void main(String[] args) throws JAXBException {
JAXBContext jaxbc = JAXBContext.newInstance(Root.class);
Unmarshaller unm = jaxbc.createUnmarshaller();
File countries = new File("countries.xml");
Root result = (Root) unm.unmarshal(countries);
}
遗憾的是,我无法让JAXB与GeneralLocation持有的子项一起运行,我得到一个UnmarshalException(无法创建GeneralLocation的实例),但我希望无论如何这都有帮助。我认为这是解决问题的更合适的方法,但它并不灵活,但要确保文档的结构是一致的。
另外你应该看看Blaise Doughan的博客:http://blog.bdoughan.com/,它包含了很多有用的JAXB开发人员信息;)
答案 2 :(得分:0)
我想建议你采用不同的方法,因为我认为你使用了错误的工具来解决问题。当您使用data projection(披露:我与此项目相关联)而不是数据绑定时,您会得到一个简短且通用的解决方案,即JHOB无法实现的恕我直言:
public class LocationExample {
public interface GeneralLocation {
@XBRead("@name")
String getName();
@XBRead("name()")
String getType();
@XBRead("./*")
List<GeneralLocation> getSons();
@XBRead("/root/Country")
List<GeneralLocation> getCountries();
}
public static void main(String... args) throws IOException {
GeneralLocation location = new XBProjector().io().url("resource://locations.xml").read(GeneralLocation.class);
printLocations(location.getCountries());
}
public static void printLocations(List<GeneralLocation> locations) {
for (GeneralLocation son:locations) {
System.out.println(son.getType()+": "+son.getName());
printLocations(son.getSons());
}
}
}
我需要在xml中添加一个根元素才能使其有效。输入
<root>
<Country name="USA">
<City name="NewYork">
<Street name="Something" />
<Street name="Something2" />
</City>
<City name="LosAngeles">
<Street name="Something" />
<Street name="Something2" />
</City>
</Country>
</root>
程序打印出来:
Country: USA
City: NewYork
Street: Something
Street: Something2
City: LosAngeles
Street: Something
Street: Something2
我知道这不是您要求的JAXB解决方案,但它更短并且有效。