使用 JAXB 在两个不同的命名空间中重用对象

Reusing object in two different namespaces with JAXB

我现在需要创建和解析一组 xml 的结构,它们在结构上非常相似,但遗憾的是已在几个不同的命名空间中定义。我通常能够使用 JAXB 解析和创建所有 XML,但似乎在让 JAXB 处理名称空间定义时遇到了一些问题。下面的简单示例应该可以说明问题。

如果我用这样的命名空间定义带注释的 class:

Bus.java:

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

并在不指定命名空间的情况下定义此 class 使用的另一个 class

Driver.java:

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

同时,我有以下包级定义,允许我在 Bus 的命名空间中使用 Driver class,而无需在 class:

包-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 class,例如像这样 ?

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,没有它,每个字段似乎都需要一个显式命名空间。

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

对于同一个包,您不能按预期更改命名空间。

抽象思想

通过xml镜头Driverhttp://www.example.org/bus看与Driverhttp://www.example.org/car不同。你不能简单地重复使用它们,那是你的问题。

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

现在,当谈到 Java(以及一般的 OOP)中的代码重用时,最流行的代码重用方式之一是继承。

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

实施

我建议以这种方式构建您的 类:

└───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 对于 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.* 类 类似于 xml.bus.* 类.

这应该适合你。