为什么JAXB不为列表生成setter

时间:2012-12-17 11:12:03

标签: java jaxb getter-setter

当我从XSD生成JAXB类时,带有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?我知道我自己可以编写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>

6 个答案:

答案 0 :(得分:26)

以下是JAXB规范的理由 - 第60页。

  

设计说明 - List属性没有setter方法。该   getter通过引用返回List。可以将项目添加到   getter方法使用适当的方法返回的列表   在java.util.List上定义。 JAXB 1.0中此设计的基本原理是   使实现能够包装列表并能够   在列表中添加或删除内容时执行检查。

因此,如果List的实现覆盖了add / remove来执行验证,那么用(例如)ArrayList替换'special'List会使这些检查失败。

答案 1 :(得分:24)

链接:No setter for list

  

getter方法中的代码确保List为   创建。没有相应的 setter ,这意味着所有   列表元素的添加或删除必须在“live”上进行   列表。

正如引言所说,当你使用getter方法时没有setter,它确保如果不存在则初始化列表的新实例。

之后,当您必须添加或删除任何内容时,您必须使用

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>

答案 2 :(得分:4)

同意Patrick对上述问题的关注。如果我直接对生成的java类进行编码,我很乐意帮忙,但我使用的是一个内省工具,需要一个setter或一个可直接访问的成员。 使用XJC的插件成功了 https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin 并向wsimport

添加-B-Xsetter参数

答案 3 :(得分:2)

一个人可以为自己的特定要求写自己的XJC plugin

在此示例中,尝试在id生成的每个Java文件中添加long类型的xjc字段:

package com.ricston;

import java.io.IOException;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

import com.sun.codemodel.JBlock;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.tools.xjc.BadCommandLineException;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.Plugin;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.Outline;

public class XJCPlugin extends Plugin {

  public final static String ID = "id";
    public final static JType LONG_TYPE = new JCodeModel().LONG;
    public final static String ID_GETTER = "getId";
    public final static JType VOID_TYPE = new JCodeModel().VOID;
    public final static String ID_SETTER = "setId";

    @Override
    public String getOptionName() {
        return "Xexample-plugin";
    }

    @Override
    public int parseArgument(Options opt, String[] args, int i)
            throws BadCommandLineException, IOException {
        return 1;
    }

    @Override
    public String getUsage() {
        return "  -Xexample-plugin    :  xjc example plugin";
    }

    @Override
    public boolean run(Outline model, Options opt, ErrorHandler errorHandler)
            throws SAXException {

        for (ClassOutline classOutline : model.getClasses()) {
            JFieldVar globalId = classOutline.implClass.field(JMod.PRIVATE,
                    LONG_TYPE, ID);

            JMethod idGetterMethod = classOutline.implClass.method(JMod.PUBLIC,
                    LONG_TYPE, ID_GETTER);
            JBlock idGetterBlock = idGetterMethod.body();
            idGetterBlock._return(globalId);

            JMethod idSetterMethod = classOutline.implClass.method(JMod.PUBLIC,
                    VOID_TYPE, ID_SETTER);
            JVar localId = idSetterMethod.param(LONG_TYPE, "_" + ID);
            JBlock idSetterBlock = idSetterMethod.body();
            idSetterBlock.assign(globalId, localId);
        }
        return true;
    }

}

完整示例here

这里还有一个例子:

https://blog.jooq.org/2018/02/19/how-to-implement-your-own-xjc-plugin-to-generate-tostring-equals-and-hashcode-methods/

它们也是可以在github生成hashCodeequalssetters-for-list的插件。

参考:

回答我所问过的question

答案 4 :(得分:1)

如果有人在这里寻找摆脱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>

对于setter,我还使用@lombok.Setter和绑定文件将org.jvnet.jaxb2_commons:jaxb2-basics-annotate添加到某些类。因此,这些类最终成为标准bean。

如果有人知道一种不那么黑客的方式,我很乐意听到它 - 例如,一个XJC插件。

答案 5 :(得分:0)

需要在插件内部添加依赖项。例如下面的

<plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>jaxb2-maven-plugin</artifactId>
                    <version>1.5</version>
                    <dependencies>
                    <dependency>
                        <groupId>org.andromda.thirdparty.jaxb2_commons</groupId>
                        <artifactId>collection-setter-injector</artifactId>
                        <version>1.0</version>
                    </dependency>
                    </dependencies>
                    <configuration>
                        <schemaDirectory>${project.basedir}/epc</schemaDirectory>
                        <arguments>-Xcollection-setter-injector</arguments>
                        <clearOutputDir>false</clearOutputDir>
                        <extension>true</extension>
                    </configuration>
                    <executions>
                        <execution>
                            <id>generate-java-from-xsd-1</id>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>xjc</goal>
                            </goals>
                            <configuration>
                                <packageName>${package name}</packageName>
                                <schemaFiles>example.xsd</schemaFiles>
                                <schemaDirectory>${project.basedir}/epc</schemaDirectory>
                                <bindingFiles>example_1.xjb</bindingFiles>
                                <bindingDirectory>${project.basedir}/generate</bindingDirectory>
                                <staleFile>${project.build.directory}/jaxb2/.xjc1StaleFlag</staleFile>
                            </configuration>
                        </execution>
              </plugin>