Почему JAXB не создает сеттеры для списков
Когда я генерирую классы JAXB из XSD, элементы с maxOccurs="unbounded"
получают генерируемый для них метод getter, но не метод setter, как показано ниже:
/**
* Gets the value of the element3 property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the element3 property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getElement3().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Type }
*
*
*/
public List<Type> getElement3() {
if (element3 == null) {
element3 = new ArrayList<Type>();
}
return this.element3;
}
Комментарий метода четко определяет, как я могу его использовать, но мой вопрос следующий:
Почему JAXB просто не генерирует сеттер, следуя правилам Java Beans? Я знаю, что сам могу написать метод setter, но есть ли какое-либо преимущество в подходе, предложенном в сгенерированном методе getter?
Это мой XSD:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.example.org/DoTransfer/" targetNamespace="http://www.example.org/DoTransfer/">
<element name="CollectionTest" type="tns:CollectionTest"></element>
<complexType name="CollectionTest">
<sequence>
<element name="element1" type="string" maxOccurs="1" minOccurs="1"></element>
<element name="element2" type="boolean" maxOccurs="1" minOccurs="1"></element>
<element name="element3" type="tns:type" maxOccurs="unbounded" minOccurs="1" nillable="true"></element>
</sequence>
</complexType>
<complexType name="type">
<sequence>
<element name="subelement1" type="string" maxOccurs="1" minOccurs="1"></element>
<element name="subelement2" type="string" maxOccurs="1" minOccurs="0"></element>
</sequence>
</complexType>
</schema>
Ответы
Ответ 1
Вот обоснование из спецификации JAXB - стр. 60.
Замечание по дизайну. Для свойства List нет метода setter. getter возвращает список по ссылке. Элемент может быть добавлен в Список, возвращаемый методом getter с использованием соответствующего метода определенный на java.util.List. Обоснование для этого проекта в JAXB 1.0 было чтобы реализация могла обернуть список и уметь выполнять проверки по мере добавления или удаления содержимого из списка.
Итак, если реализация списка была переопределяющей add/remove для выполнения проверки, заменив этот "специальный" список на (например), ArrayList победил бы эти проверки.
Ответ 2
Ссылка для: Нет настройки для списка
Код в методе getter гарантирует, что List создано. Нет соответствующего setter, что означает, что все добавления или удаления элементов списка должны быть сделаны на "live" список.
Как говорится в цитате, нет сеттера, так как при использовании метода getter он гарантирует, что новый экземпляр списка инициализируется, если он отсутствует.
И после этого, когда вам нужно добавить или удалить что-нибудь, вам придется использовать
getElement3().add(Type);
ОБНОВЛЕНИЕ: разница в сортировке для null
и пустой список
Пример, где список null
@XmlRootElement(name = "list-demo")
public class ListDemo {
@XmlElementWrapper(name = "list")
@XmlElement(name = "list-item")
private List<String> list;
}
OUTPUT будет
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo/>
Пример, где список пустой
@XmlRootElement(name = "list-demo")
public class ListDemo {
@XmlElementWrapper(name = "list")
@XmlElement(name = "list-item")
private List<String> list = new ArrayList<String>();
}
OUTPUT будет:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo>
<list/>
</list-demo>
Ответ 3
Согласитесь с озабоченностью Патрика выше. Если бы я сам кодировал сгенерированные классы Java, я был бы рад обязать, но я использую интроспективный инструмент, ожидающий либо сеттера, либо непосредственно доступный элемент.
Удалось использовать плагин для XJC из
https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin
и добавление аргумента -B-Xsetter для wsimport
Ответ 4
В случае, если кто-то ищет способ избавиться от этих раздражающих ленивых инициализаторов в коде XJC-генерации... В моем Maven POM это находится под <build><plugins>
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>remove-jaxb-generated-lazy-initializers</id>
<phase>process-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target if="${remove-jaxb-generated-lazy-initializers}">
<echo message="Running 'replaceregexp' target on generated sources..."/>
<echo message="This removes JAXB-generated lazy initializers from collection accessors."/>
<replaceregexp match="if \([\w_]+ == null\) \{\s+[\w_]+ = new ArrayList<[\w_]+>\(\);\s+\}\s+" replace="" flags="g">
<fileset dir="${project.build.directory}/generated-sources" includes="**/*.java"/>
</replaceregexp>
</target>
</configuration>
</execution>
</executions>
</plugin>
Для сеттеров я также добавляю @lombok.Setter
к определенным классам, используя org.jvnet.jaxb2_commons:jaxb2-basics-annotate
и файл привязок. Таким образом, классы становятся стандартными beans.
Я хотел бы услышать это, если кто-нибудь знает о менее хакерском способе - например, плагин XJC.