64

当我从 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>
4

6 回答 6

33

这是来自 JAXB 规范的理由 - 第 60 页。

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

因此,如果 List 的实现覆盖了 add/remove 来执行验证,那么用(例如)ArrayList 替换那个“特殊”的 List 会破坏这些检查。

于 2014-08-26T13:34:10.893 回答
31

链接:列表没有设置器

getter 方法中的代码确保创建了 List。没有相应的设置器,这意味着列表元素的所有添加或删除都必须在“活动” 列表中进行。

正如报价所说,没有设置器,因为当您使用 getter 方法时,它确保列表的新实例在不存在时被初始化。

之后,当您必须添加或删除任何您必须使用的东西时

getElement3().add(Type);

更新:编组null和空列表的区别

列表为的示例null

@XmlRootElement(name = "list-demo")
public class ListDemo {

    @XmlElementWrapper(name = "list")
    @XmlElement(name = "list-item")
    private List<String> list;

}

输出将是

<?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>();

}

输出将是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo>
    <list/>
</list-demo>
于 2012-12-17T12:21:41.347 回答
4

同意以上帕特里克的担忧。如果我直接对生成的 java 类进行编码,我很乐意提供帮助,但我使用的内省工具需要一个 setter 或一个可直接访问的成员。成功使用来自 https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin的 XJC 插件 并将 -B-Xsetter 参数添加到 wsimport

于 2016-10-14T01:19:40.543 回答
2

XJC plugin可以根据自己的具体要求编写自己的。

在此示例中,尝试在每个生成的 java 文件中添加一个id类型字段:longxjc

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;
    }

}

完整的例子在这里

这里的另一个例子:

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

它们也是可在githubhashCode上生成, equals,的插件。setters-for-list

参考:

回答我提出的一个问题

于 2018-07-19T11:54:55.253 回答
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>

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

如果有人知道一种不那么老套的方法,我很乐意听到它——例如,一个 XJC 插件。

于 2017-11-25T09:24:36.770 回答
1

需要在插件内部添加依赖。例如如下,

<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>

于 2020-06-30T12:20:42.117 回答