使用JAXB编组/解组Java超类和子类

时间:2015-10-27 13:48:55

标签: java xml jaxb unmarshalling

我一直在试验JAXB教程,并设法让代码工作,从Java对象生成XML文件,然后能够使用XML生成Java对象。目前,它读取同一类的多个实例,以创建类似于下面的

的XML文件
<Car>
    <regplate>TR54</regplate>
    <colour>red</colour>
    <energyrating>5</energyrating>
</Car>
<Car>
    <regplate>BN04 THY</regplate>
    <colour>yellow</colour>
    <energyrating>3</energyrating>
</Car>
<Car>
    <regplate>BN05 THY</regplate>
    <colour>yellow</colour>
    <energyrating>5</energyrating>
</Car>

我希望能够使用JAXB技术来处理子类。例如:假设我有一个Car,Van和Bicycle对象,它们是Vehicle的子类。我是否可以操作我的JAXB类来编写一个可以产生类似内容的XML文件?我已经提供了我正在使用的代码。

<Vehicle>
    <Car>
        <regplate>TR54</regplate>
        <colour>red</colour>
        <energyrating>5</energyrating>
    </Car>
    <Van>
        <regplate>MN05 RFD</regplate>
        <colour>red</colour>
        <energyrating>5</energyrating>
    </Van>
    <Car>
        <regplate>ZX54 UJK</regplate>
        <colour>red</colour>
        <energyrating>1</energyrating>
    </Car>
</Vehicle>

主类

package basictransport2;

public class Main
{
    public static void main(String[] args)
    {
        JAXB parser = new JAXB();
        parser.marshall();
        //parser.unmarshallList();
    }
}

车辆类

package basictransport2;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

//@XmlRootElement(name = "Vehicle")
public class Vehicle
{
    private int ownerId;

    public Vehicle(int ownerId)
    {
        this.setOwnerId(ownerId);
    }

    //@XmlElement (name = "Owner ID")
    public int getOwnerId()
    {
        return ownerId;
    }

    public void setOwnerId(int ownerId)
    {
        this.ownerId = ownerId;
    }


    public int getEnergyRating()
    {
        return (Integer) null;
    }


    public String getColour()
    {
        return null;
    }

    public String getRegPlate()
    {
        return null;
    }
}

汽车类

package basictransport2;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

//@XmlRootElement(name = "Car")
public class Car extends Vehicle
{
    private String regPlate;
    private int energyRating;
    private String colour;

    public Car(String regPlate, int energyRating, String colour, int ownerId)
    {
        super(ownerId);
        this.regPlate = regPlate;
        this.energyRating = energyRating;
        this.colour = colour;
    } 

    public Car(int ownerId)
    {
        super(ownerId);
    }

    //@XmlElement (name = "Registration")
    public String getRegPlate()
    {
        return regPlate;
    }

    public void setRegPlate(String regPlate)
    {
        if(this.regPlate == null)
        {
            this.regPlate = regPlate;
        }
    }

    //@XmlElement (name = "Energy Rating")
    public int getEnergyRating()
    {
        return energyRating;
    }

    public void setEnergyRating(int energyRating)
    {
        this.energyRating = energyRating;
    }

    //@XmlElement (name = "Colour")
    public String getColour()
    {
        return colour;
    }

    public void setColour(String colour)
    {
        this.colour = colour;
    }
}

JAXB Class

package basictransport2;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class JAXB
{   
    public void marshall()
    {
        try
        {
            List<Vehicle> vehicleList = new ArrayList<Vehicle>();

            vehicleList.add(new Car("SG09 TYH", 4, "Yellow", 1));
            vehicleList.add(new Car("XX09 VVV", 3, "Red", 2));
            vehicleList.add(new Car("BL09 TYZ", 4, "Blue", 3));

            Garage listOfVehicles = new Garage();
            listOfVehicles.setListOfVehicles(vehicleList);

            JAXBContext context = JAXBContext.newInstance(Garage.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(listOfVehicles, System.out);
            marshaller.marshal(listOfVehicles, new File("src\\data\\listcar.xml"));
        }

        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
    }

    public void unmarshall()
    {
        try
        {
            JAXBContext context = JAXBContext.newInstance(Garage.class);
            Unmarshaller unmarhsaller = context.createUnmarshaller();
            Garage listOfVehicles = (Garage)unmarhsaller.unmarshal(new File("src\\data\\listcar.xml"));
            System.out.println("List Car information");

            for(Vehicle vehicle : listOfVehicles.getListOfVehicles())
            {
                System.out.println("Reg Plate: " + vehicle.getRegPlate());
                System.out.println("Energy Rating: " + vehicle.getEnergyRating());
                System.out.println("Colour: " + vehicle.getColour());
                System.out.println("================");
            }    
        }

        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
    }
}

列出班级

package basictransport2;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="Vehicle")
public class Garage
{
    @XmlElements
    ({
        @XmlElement(name = "Car", type = Car.class, required = false)
    })    

    private List<Vehicle> vehicleCollection = new ArrayList<Vehicle>();

    public List<Vehicle> getListOfVehicles()
    {
        return vehicleCollection;
    }

    public void setListOfVehicles(List<Vehicle> listOfVehicles)
    {
        this.vehicleCollection = listOfVehicles;
    }
}

4 个答案:

答案 0 :(得分:3)

感谢大家的意见。我使用了你所有答案的反馈,但最终它们的结合起了作用,这就是为什么我为将来可能遇到这个问题的人创造了一个单独的答案。

为了使这个工作,我必须确保使用@XmlElement注释超级和子类中的所有getter方法被marhsalled / unmarshalled。这将确定相应变量的XML标记。

@XmlElement (name = "OwnerID")
    public int getOwnerId()
    {
        return ownerId;
    }

必须使用@XmlSeeAlso注释超类以将子类绑定到它。在我的代码RoadVehicle中是超类,CarVan类都扩展了它。

@XmlSeeAlso({Car.class, Van.class})
public class Vehicle
{

现在,超级和子类被注释,唯一需要注释的其他类是列表类(我的代码中的Garage)。此处的更改将确定填充XML标记的内容。

通过将@XmlRootElement注释应用于类的顶部来设置根XML标记。即“Vehicle”将是我示例中的根XML标记。

@XmlRootElement(name = "Vehicle")
public class Garage
{

最后,必须使用@XmlElements注释声明@XmlElements列表,每个子类需要一个XML标记,其中name提供XML标记的名称。必须在集合的getter方法之上声明此列表。

@XmlElements
    ({
        @XmlElement(name = "Car", type = Car.class, required = false),
        @XmlElement(name = "Van", type = Van.class, required = false)
    })    
    public List<Vehicle> getListOfVehicles()
    {
        return vehicleCollection;
    }

答案 1 :(得分:1)

你走在正确的轨道上。下面的内容可能会有所帮助

@XmlRootElement(name = "car")
public class Car extends BasicType{

}

@XmlRootElement(name = "van")
public class Van extends BasicType{

}

@XmlRootElement(name = "vehicle")
    public class Vehicle {
         List<BasicType> basicType;


    }

答案 2 :(得分:1)

最简单的解决方案是为汽车和货车设置不同的子类,即使它们没有向基类添加任何东西。然后,根元素类包含基类的列表,其中元素QNames标识实际的类。

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Vehicle")
public class Vehicle {
    @XmlElements({
        @XmlElement(name = "Car", type = Car.class, required = false),
        @XmlElement(name = "Van", type = Van.class, required = false)
    })
    protected List carOrVan;
    public List getCarOrVan() {
        if (carOrVan == null) {
            carOrVan = new ArrayList();
        }
        return this.carOrVan;
    }
}

这里是基类和子类:

public class Basic {
    private String regplate;
    private String color;
    private String energyrating;

    public String getRegplate(){ return regplate; }
    public void setRegplate( String v ){ regplate = v; }
    public String getColor(){ return color; }
    public void setColor( String v ){ color = v; }
    public String getEnergyrating(){ return energyrating; }
    public void setEnergyrating( String v ){ energyrating = v; }
}

public class Car extends Basic {}

public class Van extends Basic {}

如果汽车和货车发展成不同的子类,这将会顺利进行。

答案 3 :(得分:1)

这个问题类似于我的: 使用JAXB编组/解组Java超类和子类 Marshalling/Unmarshalling Java superclass and subclasses using JAXB 使用@XmlElements批注,可以指定多个@ XmlElement / class条目。这意味着您可以为不同的@XmlElement名称使用不同的目标类。

@XmlElementWrapper( name = "elements" )
@XmlElements
( {
    @XmlElement( name="el1", type=ChildListElement1.class ),
    @XmlElement( name="el2", type=ChildListElement2.class )
} )

但是,这仍然意味着超类必须了解子类。每次创建子类时,也都需要修改超类,以使该子类成为JAXB目标类。

我阅读了有关IDResolver.bind,jaxb:bindings,SchemaFactory,AnnotationHelper,StreamReaderDelegate等的各种文章。它们似乎都不允许在编组期间为给定的节点名称指定目标类。不幸的是,在解组期间会忽略绑定中指定的类。要么是我不理解,要么是编码实验没有正确执行。 ;-)

因此,我决定使用一种解决方法。基本上,将setter / getter和相应的@XmlElements移动到子类。为了增加想象力,将抽象设置器/获取器添加到超类。并不是我真正想要的解决方案,但是IMO比将子类硬编码为通用超类要好一些。

public abstract class ParentBucket<LE extends ParentListElement>
{
    List<LE> elementList;

    public abstract void setElementList(List<LE> elementList);
    public abstract List<LE> getElementList();

    protected void setElementListCore(List<LE> elementList)
    {
        this.elementList = elementList;
    }
    public List<LE> getElementListCore()
    {
        return elementList;
    }
}


@XmlRootElement ( name = "bucket" )
public class ChildBucket1 extends ParentBucket<ChildListElement1>
{
    @Override
    @XmlElementWrapper( name = "elements" )
    @XmlElements ( { @XmlElement( name="element", type=ChildListElement1.class ) } )
    public void setElementList(List<ChildListElement1> elementList)
    {
        super.setElementListCore ( elementList );
    }
    @Override
    public List<ChildListElement1> getElementList()
    {
        return super.getElementListCore();
    }
}


@XmlRootElement
public class ChildBucket2 extends ParentBucket<ChildListElement2>
{
    @Override
    @XmlElementWrapper( name = "elements" )
    @XmlElements ( { @XmlElement( name="element", type=ChildListElement2.class ) } )
    public void setElementList(List<ChildListElement2> elementList)
    {
        super.setElementListCore ( elementList );
    }
    @Override
    public List<ChildListElement2> getElementList()
    {
        return super.getElementListCore();
    }
}