Java XSD - использование настраиваемого типа коллекции
Моя проблема
Я использую плагин jaxb2 maven для преобразования моих объектов, определенных XSD, в классы Java. Моя цель - установить элемент типа списка в моем XSD (например,
xs:choice unbound
) в LinkedList вместо использования типа ArrayList по умолчанию. Я использую jaxb-xew-plugin версии 1.10. Вот мой соответствующий код:
XSD:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
jaxb:version="2.0"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xew="http://github.com/jaxb-xew-plugin"
jaxb:extensionBindingPrefixes="xew"
elementFormDefault="qualified">
<xs:element name="TEST">
<xs:complexType>
<xs:sequence maxOccurs="unbounded">
<xs:element ref="action"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="action">
<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:sequence>
<xs:element name="order" type="xs:nonNegativeInteger"/>
<xs:choice maxOccurs="unbounded">
<xs:element name="x" type="xs:token"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
ПОМ:
<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:sequence maxOccurs="unbounded">
<xs:element ref="action">
<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>
Эта комбинация приводит к:
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
Или же...
Есть ли другой способ использовать настраиваемый тип списка при создании моих объектов Java из XSD?
(PS. Я разместил это в разделе github для jaxb-xew-plugin, однако я понял, что последняя фиксация была более двух лет назад, поэтому это может быть бездействующий проект. Публикация здесь для помощи в сообществе SOF)
2 ответа
Вам необходимо настроить привязки JAXB. Итак, в каталоге вашего проекта скажем
src/main/xjb
, создайте такой файл (остерегайтесь
collectionType
):
<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb">
<jxb:globalBindings collectionType="java.util.Linkedlist"/>
</jxb:bindings>
Затем используйте его в конфигурации плагина jaxb2 maven в своем POM:
...
<configuration>
<sources>src/main/xsd</sources>
<xjbSources>
<xjbSource>src/main/xjb</xjbSource>
</xjbSources>
...
</configuration>
...
В рамках предоставленного вами XSD настройка имеет смысл. Вы должны понимать роль плагина Xew: если тип A имеет какое-то поле, которое является сложным, как тип TEST в нашем примере, тогда у нас есть "матроска" A→TEST→List, и в этом случае плагин пытается удалить тип TEST из модель. Теперь вы предоставили в своем примере только ТЕСТ, это только половина дела, плагин в этом случае ничего не делает. Вам также необходимо предоставить другой тип A, который использует TEST, например:
<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.
и результирующий класс будет (я удалил несвязанный персонал):
@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 самостоятельно, вам необходимо использовать собственный набор настроек JAXB 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>