使用JAXB

时间:2017-09-20 19:19:17

标签: java xml jaxb xml-namespaces

我需要创建和解析一组结构非常相似但不幸在几个不同命名空间中定义的xml。我通常能够使用JAXB解析和创建所有XML,但似乎在使JAXB处理命名空间定义方面存在一些问题。以下简单示例应说明问题。

如果我使用如下命名空间定义带注释的类:

Bus.java:

@XmlRootElement(name = "Bus", namespace = "http://www.example.org/bus")
@XmlAccessorType(XmlAccessType.FIELD)
public class Bus {
    @XmlElement(name = "Driver")
    Driver driver;
}

定义此类使用的另一个类,而不指定命名空间

Driver.java:

@XmlAccessorType(XmlAccessType.FIELD)
public class Driver {
    @XmlElement(name = "DriverName")
    String driverName;
}

同时我有以下包级别定义,允许我在Driver的命名空间中使用Bus类,而无需在类中明确定义它的命名空间: / p>

package-info.java

@XmlSchema(namespace = "http://www.example.org/bus", 
  xmlns = { @XmlNs(prefix = "", namespaceURI = "http://www.example.org/bus") }
, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)

我有一个像这样的xml:

String xml = "<Bus xmlns=\"http://www.example.org/bus\">" + 
             "  <Driver>" +
             "    <DriverName>Bob</DriverName>" +
             "  </Driver>" + 
             "</Bus>";

然后我可以使用以下内容简单地解析/重新创建xml:

JAXBContext context = JAXBContext.newInstance(Bus.class);

// parse xml
Unmarshaller um = context.createUnmarshaller();
Object xmlObj = um.unmarshal(new StringReader(xml));

// recreate xml 
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
marshaller.marshal(xmlObj, sw);
String recreatedXml = sw.toString();

System.out.println("recreatedXml);

这给出了:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Bus xmlns="http://www.example.org/bus">
    <Driver>
        <DriverName>Bob</DriverName>
    </Driver>
</Bus>

但是如何重新使用另一个命名空间中的Driver类,例如像这样 ?

Car.java

@XmlRootElement(name = "Car", namespace = "http://www.example.org/car")
@XmlAccessorType(XmlAccessType.FIELD)
public class Car {
    @XmlElement(name = "Driver")
    Driver driver;
}

package-info.java只允许一个XmlSchema,没有它,每个字段似乎都需要一个显式的命名空间。

2 个答案:

答案 0 :(得分:0)

您已经编写了另一个包和package-info,您应该为Car定义xml-schema。您可以轻松地使用该包中的常用类。

从同一个包中,您无法按预期更改命名空间。

答案 1 :(得分:0)

抽象的想法

Driver查看xml镜头http://www.example.org/busDriver中的http://www.example.org/car不同。你不能简单地重用它们,这就是你的问题。

您正在使用JAXB,它允许您在Java类和xml元素之间建立maping。这使您可以从Java类的角度查看相同的问题。

现在,在谈论Java中的代码重用(通常是OOP)时,最常用的代码重用方法之一就是继承。

因此,核心思想是使用继承来共享来自不同命名空间的Driver之间的“代码”(在您的情况下只是属性定义)。

<强>实施

我建议用这种方式构建你的类:

└───xml
    │   DriverBase.java
    │
    ├───bus
    │       Bus.java
    │       Driver.java
    │       package-info.java
    │
    └───car
            Car.java
            Driver.java
            package-info.java

...其中xml.DriverBasexml.bus.Driverxml.car.Driver的基础,如下所示:

package xml;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;

@XmlTransient
@XmlAccessorType(XmlAccessType.PROPERTY)
abstract public class DriverBase {
    @XmlElement(name = "DriverName")
    String driverName;
}

请注意@XmlTransient的使用,因为我们实际上并不希望JAXB映射DriverBase,只映射其子类。

xml.bus.Driver只是继承了xml.DriverBase,应该是这样的:

package xml.bus;

import xml.DriverBase;

public class Driver extends DriverBase {
}

xml.bus.Bus与以前大致相同:

package xml.bus;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "Bus", namespace = "http://www.example.org/bus")
@XmlAccessorType(XmlAccessType.FIELD)
public class Bus {
    @XmlElement(name = "Driver")
    Driver driver;
}

...以及package-info.java的{​​{1}}:

xml.bus

@javax.xml.bind.annotation.XmlSchema( namespace = "http://www.example.org/bus", xmlns = { @javax.xml.bind.annotation.XmlNs(prefix = "", namespaceURI = "http://www.example.org/bus") }, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED ) package xml.bus; 类类似于xml.car.*类。

这应该适合你。