使用 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镜头Driver
从http://www.example.org/bus
看与Driver
从http://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.DriverBase
是 xml.bus.Driver
和 xml.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.*
类.
这应该适合你。
我现在需要创建和解析一组 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镜头Driver
从http://www.example.org/bus
看与Driver
从http://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.DriverBase
是 xml.bus.Driver
和 xml.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.*
类.
这应该适合你。