Подтвердить что ты не робот

Почему 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>
4b9b3361

Ответ 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&lt;[\w_]+&gt;\(\);\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.