Java JAXB-对动态标记名使用继承
我正试图使用JAXB将XML封装到类Heiarcy中,我希望Heiarcy以一种通用的方式使用继承。我会更详细地解释: 我有以下XML:Java JAXB-对动态标记名使用继承,java,xml,inheritance,jaxb,Java,Xml,Inheritance,Jaxb,我正试图使用JAXB将XML封装到类Heiarcy中,我希望Heiarcy以一种通用的方式使用继承。我会更详细地解释: 我有以下XML: <Country name="USA"> <City name="NewYork"> <Street name="Something"/> <Street name="Something2"/> </City> <City name="LosAng
<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>
我的解决方案并不像您希望的那样通用,但我想它可能会帮助您理解为什么您会收到各种异常(我自己在尝试找到解决方案时遇到了许多异常)
首先,我使用以下XML作为测试(我假设您忽略了根元素):
我去掉了@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;
}
}
有了它,您就可以全部设置为取消对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所持有的子级一起运行,我得到了一个UnmarshaleException(无法创建GeneralLocation的实例),但我希望这能有所帮助。我认为这是解决问题的一种更合适的方法,虽然它没有那么灵活,但可以确保文档的结构是一致的
您还应该查看Blaise Doughan博客:,它包含了许多对JAXB开发人员有用的信息;) 我想向您建议一种不同的方法,因为我认为您在解决问题时使用了错误的工具。当您使用(披露:我与这个项目有关联)而不是数据绑定时,您会得到一个简短而通用的解决方案,这是JAXB不可能实现的:
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());
}
}
}
我知道这不是您所要求的JAXB解决方案,但它比较短而且有效。您可以在您的用例中使用@xmlementref
注释
Java模型
一般分配
您可以在sons
属性上使用@xmlementref
注释。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;
}
}
城市
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。请注意,由于我们在GeneralLocation
类上使用了@xmlseea
注释,因此在引导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/Output
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="Street")
public class Street extends GeneralLocation {
}
您问题中的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>
您可能对我对这个问题的回答感兴趣,我使用@xmlementref
映射原始模型:@Blaise这确实是一个更好的方法,我读了@xmlsee也
但没有弄清楚如何使它工作。谢谢。@xmlseea
没有太大的魔力,它只是告诉JAXB也为其他类生成元数据(这里使用,因为在Java中不能反射地确定子类)。关键是@xmlementref
和@XmlRootElement
的使用。这正是我想要的。谢谢
Country: USA
City: NewYork
Street: Something
Street: Something2
City: LosAngeles
Street: Something
Street: Something2
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;
}
}
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 {
}
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);
}
}
<?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>