Java XSD - 使用自定义集合类型
Java XSD - using a custom collection type
我的问题
我正在使用 jaxb2 maven 插件将我的 XSD 定义的对象转换为 Java 类。我的目标是在我的XSD(比如xs:choice unbound
)中设置一个列表类型的元素为LinkedList,而不是使用默认的ArrayList类型。我正在使用 jaxb-xew-plugin 版本 1.10。这是我的相关代码:
XSD:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema jaxb:version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xew="http://github.com/jaxb-xew-plugin"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
jaxb:extensionBindingPrefixes="xew"
elementFormDefault="qualified">
<xs:element name="TEST">
<xs:complexType>
<xs:annotation>
<xs:appinfo>
<xew:xew collection="java.util.LinkedList"
collectionInterface="java.util.List"
instantiate="lazy"
plural="true"/>
</xs:appinfo>
</xs:annotation>
<xs:choice>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded">
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
POM:
<build>
<resources>
<resource>
<directory>src/main/xsd</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<phase>generate-sources</phase>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<sources>src/main/xsd</sources>
<packageName>com.tug.data.model.gen</packageName>
<verbose>true</verbose>
<clearOutputDir>false</clearOutputDir>
<extension>true</extension>
<arguments>
<argument>-Xsetters</argument>
<argument>-Xxew</argument>
<argument>-Xfluent-api</argument>
<argument>-Xjaxbindex</argument>
<argument>-Xequals</argument>
<argument>-XhashCode</argument>
<argument>-XtoString</argument>
<argument>-Xcopyable</argument>
<argument>-Xmergeable</argument>
</arguments>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.github.jaxb-xew-plugin</groupId>
<artifactId>jaxb-xew-plugin</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>net.java.dev.jaxb2-commons</groupId>
<artifactId>jaxb-fluent-api</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.12.0</version>
</dependency>
</dependencies>
</plugin>
结果 Java 代码生成
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"action"
})
@XmlRootElement(name = "TEST")
public class TEST implements Cloneable, CopyTo2, Equals2, HashCode2, MergeFrom2, ToString2
{
@XmlElement(required = true)
protected List<Action> action;
public List<Action> getAction() {
if (action == null) {
action = new ArrayList<Action>(); // <--- **This should have been LinkedList**
}
return this.action;
}
如您所见,ArrayList
类型仍然出现,而不是 LinkedList
。实际上 xew
参数和命令似乎被完全忽略了......我没有收到任何错误
我已经尝试了很多变体,复制并粘贴了 xs:annotation
简介几乎所有我能合理想到的位置组合。我得到的唯一错误是:
<xs:element name="TEST">
<xs:complexType>
<xs:choice>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:appinfo>
<xew:xew collection="java.util.LinkedList"
collectionInterface="java.util.List"
instantiate="lazy"
plural="true"/>
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
这个组合的结果是:com.sun.istack.SAXParseException2: compiler was unable to honor this xew customization. It is attached to a wrong place, or its inconsistent with other bindings.
您是否看到任何遗漏的步骤会导致我的自定义集合覆盖不会被拾取?
我附上了 maven 调试输出,需要查看的内容很多,但我没有从中找到任何提示。
mvn-debug.log
或者...
在从 XSD 生成我的 Java 对象时,还有其他方法可以使用自定义列表类型吗?
(PS。我在 jaxb-xew-plugin 的 github 部分 cross-posted this,但是我意识到最后一次提交是在 2 年前,所以它可能是休眠项目。在此发帖寻求 SOF 社区的帮助)
您必须自定义 JAXB 绑定。所以在你的项目目录中,比如 src/main/xjb
,创建一个这样的文件(注意 collectionType
):
<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb">
<globalBindings collectionType="java.util.Linkedlist"/>
</bindings>
这将全局自定义 Java 类型(针对所有架构中的所有元素)。
如果您只想针对特定元素进行自定义,例如action
元素在你的情况下,指定模式位置和 XPath 到这样的元素(例如假设 xsd 是 src/main/xsd
文件夹):
<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<bindings schemaLocation="../xsd/schema.xsd" node="//xs:element[@name='action']">
<property collectionType="java.util.LinkedList" />
</bindings>
</bindings>
然后在 POM 的 jaxb2 maven 插件配置中使用它:
...
<configuration>
<sources>src/main/xsd</sources>
<xjbSources>
<xjbSource>src/main/xjb</xjbSource>
</xjbSources>
...
</configuration>
...
XML 架构未被修改并保持原样 platform/language-independent。
在您提供的 XSD 范围内,自定义确实有意义。您需要了解 Xew 插件的作用:如果类型 A 有一些复杂的字段,如我们示例中的类型 TEST,那么我们有一个“matroska”A→TEST→List,在这种情况下,插件会尝试从中删除类型 TEST该模型。
现在你在你的例子中只提供了 TEST,这只是故事的一半,插件在这种情况下不做任何事情。您还需要提供另一个使用 TEST 的类型 A,例如:
<xs:element name="A">
<xs:complexType>
<xs:sequence>
<xs:element ref="TEST">
<xs:annotation>
<xs:appinfo>
<xew:xew collection="java.util.LinkedList"
collectionInterface="java.util.List"
instantiate="lazy"
plural="true"/>
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="TEST">
<xs:complexType>
<xs:sequence>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
这就是插件启动的地方。您将在输出中看到:
[INFO] Modifications:
[INFO] Replacing field [TEST com.tug.data.model.gen.A#test]
[INFO] 1 modification(s) to original code.
[INFO]
[INFO] Deletions:
[INFO] Removing class com.tug.data.model.gen.TEST from package com.tug.data.model.gen
[INFO] Removing factory method [com.tug.data.model.gen.TEST#createTEST()] from com.tug.data.model.gen.ObjectFactory
[INFO] 2 deletion(s) from original code.
结果 class 将是(我删除了不相关的人员):
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"tests"
})
@XmlRootElement(name = "A")
public class A implements Cloneable, CopyTo2, Equals2, HashCode2, MergeFrom2, ToString2
{
@XmlElementWrapper(name = "TEST", required = true)
@XmlElement(name = "action")
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
protected List<String> tests;
public List<String> getTESTS() {
if (tests == null) {
tests = new LinkedList<String>();
}
return tests;
}
public void setTESTS(List<String> tests) {
this.tests = tests;
}
public A withTESTS(String... values) {
if (values!= null) {
for (String value: values) {
getTESTS().add(value);
}
}
return this;
}
public A withTESTS(Collection<String> values) {
if (values!= null) {
getTESTS().addAll(values);
}
return this;
}
public A withTESTS(List<String> tests) {
setTESTS(tests);
return this;
}
...
}
这就是 LinkedList
弹出的地方。
所以插件不会修改测试类型。基本上,该政策将是“删除或保留它”。如果你想自己自定义类型TEST,你需要使用native JAXB customization collectionType:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema jaxb:version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="TEST">
<xs:complexType>
<xs:sequence>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:appinfo>
<jaxb:property collectionType="java.util.LinkedList" />
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
我的问题
我正在使用 jaxb2 maven 插件将我的 XSD 定义的对象转换为 Java 类。我的目标是在我的XSD(比如xs:choice unbound
)中设置一个列表类型的元素为LinkedList,而不是使用默认的ArrayList类型。我正在使用 jaxb-xew-plugin 版本 1.10。这是我的相关代码:
XSD:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema jaxb:version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xew="http://github.com/jaxb-xew-plugin"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
jaxb:extensionBindingPrefixes="xew"
elementFormDefault="qualified">
<xs:element name="TEST">
<xs:complexType>
<xs:annotation>
<xs:appinfo>
<xew:xew collection="java.util.LinkedList"
collectionInterface="java.util.List"
instantiate="lazy"
plural="true"/>
</xs:appinfo>
</xs:annotation>
<xs:choice>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded">
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
POM:
<build>
<resources>
<resource>
<directory>src/main/xsd</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<phase>generate-sources</phase>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<sources>src/main/xsd</sources>
<packageName>com.tug.data.model.gen</packageName>
<verbose>true</verbose>
<clearOutputDir>false</clearOutputDir>
<extension>true</extension>
<arguments>
<argument>-Xsetters</argument>
<argument>-Xxew</argument>
<argument>-Xfluent-api</argument>
<argument>-Xjaxbindex</argument>
<argument>-Xequals</argument>
<argument>-XhashCode</argument>
<argument>-XtoString</argument>
<argument>-Xcopyable</argument>
<argument>-Xmergeable</argument>
</arguments>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.github.jaxb-xew-plugin</groupId>
<artifactId>jaxb-xew-plugin</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>net.java.dev.jaxb2-commons</groupId>
<artifactId>jaxb-fluent-api</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>org.jvnet.jaxb2_commons</groupId>
<artifactId>jaxb2-basics</artifactId>
<version>0.12.0</version>
</dependency>
</dependencies>
</plugin>
结果 Java 代码生成
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"action"
})
@XmlRootElement(name = "TEST")
public class TEST implements Cloneable, CopyTo2, Equals2, HashCode2, MergeFrom2, ToString2
{
@XmlElement(required = true)
protected List<Action> action;
public List<Action> getAction() {
if (action == null) {
action = new ArrayList<Action>(); // <--- **This should have been LinkedList**
}
return this.action;
}
如您所见,ArrayList
类型仍然出现,而不是 LinkedList
。实际上 xew
参数和命令似乎被完全忽略了......我没有收到任何错误
我已经尝试了很多变体,复制并粘贴了 xs:annotation
简介几乎所有我能合理想到的位置组合。我得到的唯一错误是:
<xs:element name="TEST">
<xs:complexType>
<xs:choice>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:appinfo>
<xew:xew collection="java.util.LinkedList"
collectionInterface="java.util.List"
instantiate="lazy"
plural="true"/>
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
这个组合的结果是:com.sun.istack.SAXParseException2: compiler was unable to honor this xew customization. It is attached to a wrong place, or its inconsistent with other bindings.
您是否看到任何遗漏的步骤会导致我的自定义集合覆盖不会被拾取?
我附上了 maven 调试输出,需要查看的内容很多,但我没有从中找到任何提示。 mvn-debug.log
或者...
在从 XSD 生成我的 Java 对象时,还有其他方法可以使用自定义列表类型吗?
(PS。我在 jaxb-xew-plugin 的 github 部分 cross-posted this,但是我意识到最后一次提交是在 2 年前,所以它可能是休眠项目。在此发帖寻求 SOF 社区的帮助)
您必须自定义 JAXB 绑定。所以在你的项目目录中,比如 src/main/xjb
,创建一个这样的文件(注意 collectionType
):
<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb">
<globalBindings collectionType="java.util.Linkedlist"/>
</bindings>
这将全局自定义 Java 类型(针对所有架构中的所有元素)。
如果您只想针对特定元素进行自定义,例如action
元素在你的情况下,指定模式位置和 XPath 到这样的元素(例如假设 xsd 是 src/main/xsd
文件夹):
<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<bindings schemaLocation="../xsd/schema.xsd" node="//xs:element[@name='action']">
<property collectionType="java.util.LinkedList" />
</bindings>
</bindings>
然后在 POM 的 jaxb2 maven 插件配置中使用它:
...
<configuration>
<sources>src/main/xsd</sources>
<xjbSources>
<xjbSource>src/main/xjb</xjbSource>
</xjbSources>
...
</configuration>
...
XML 架构未被修改并保持原样 platform/language-independent。
在您提供的 XSD 范围内,自定义确实有意义。您需要了解 Xew 插件的作用:如果类型 A 有一些复杂的字段,如我们示例中的类型 TEST,那么我们有一个“matroska”A→TEST→List,在这种情况下,插件会尝试从中删除类型 TEST该模型。 现在你在你的例子中只提供了 TEST,这只是故事的一半,插件在这种情况下不做任何事情。您还需要提供另一个使用 TEST 的类型 A,例如:
<xs:element name="A">
<xs:complexType>
<xs:sequence>
<xs:element ref="TEST">
<xs:annotation>
<xs:appinfo>
<xew:xew collection="java.util.LinkedList"
collectionInterface="java.util.List"
instantiate="lazy"
plural="true"/>
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="TEST">
<xs:complexType>
<xs:sequence>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
这就是插件启动的地方。您将在输出中看到:
[INFO] Modifications:
[INFO] Replacing field [TEST com.tug.data.model.gen.A#test]
[INFO] 1 modification(s) to original code.
[INFO]
[INFO] Deletions:
[INFO] Removing class com.tug.data.model.gen.TEST from package com.tug.data.model.gen
[INFO] Removing factory method [com.tug.data.model.gen.TEST#createTEST()] from com.tug.data.model.gen.ObjectFactory
[INFO] 2 deletion(s) from original code.
结果 class 将是(我删除了不相关的人员):
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"tests"
})
@XmlRootElement(name = "A")
public class A implements Cloneable, CopyTo2, Equals2, HashCode2, MergeFrom2, ToString2
{
@XmlElementWrapper(name = "TEST", required = true)
@XmlElement(name = "action")
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
protected List<String> tests;
public List<String> getTESTS() {
if (tests == null) {
tests = new LinkedList<String>();
}
return tests;
}
public void setTESTS(List<String> tests) {
this.tests = tests;
}
public A withTESTS(String... values) {
if (values!= null) {
for (String value: values) {
getTESTS().add(value);
}
}
return this;
}
public A withTESTS(Collection<String> values) {
if (values!= null) {
getTESTS().addAll(values);
}
return this;
}
public A withTESTS(List<String> tests) {
setTESTS(tests);
return this;
}
...
}
这就是 LinkedList
弹出的地方。
所以插件不会修改测试类型。基本上,该政策将是“删除或保留它”。如果你想自己自定义类型TEST,你需要使用native JAXB customization collectionType:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema jaxb:version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="TEST">
<xs:complexType>
<xs:sequence>
<xs:element name="action" type="xs:token" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:appinfo>
<jaxb:property collectionType="java.util.LinkedList" />
</xs:appinfo>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>