Java JAXB-对动态标记名使用继承

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

我正试图使用JAXB将XML封装到类Heiarcy中,我希望Heiarcy以一种通用的方式使用继承。我会更详细地解释:

我有以下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>


我的解决方案并不像您希望的那样通用,但我想它可能会帮助您理解为什么您会收到各种异常(我自己在尝试找到解决方案时遇到了许多异常)

首先,我使用以下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>