簡體   English   中英

為什么 JAXB 不為 Lists 生成 setter

[英]Why doesn't JAXB generate setters for Lists

當我從 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>

這是 JAXB 規范的理由 - 第 60 頁。

設計說明 – List 屬性沒有 setter 方法。 getter 通過引用返回 List。 可以使用 java.util.List 上定義的適當方法將項添加到 getter 方法返回的 List。 JAXB 1.0 中這種設計的基本原理是使實現能夠包裝列表,並能夠在從列表中添加或刪除內容時執行檢查。

因此,如果 List 的實現覆蓋添加/刪除以執行驗證,則用(例如)ArrayList 替換該“特殊”列表將失敗這些檢查。

鏈接: 列表沒有設置器

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>

同意上面帕特里克的擔憂。 如果我直接對生成的 java 類進行編碼,我很樂意這樣做,但我使用的內省工具需要一個 setter 或一個可直接訪問的成員。 成功使用了來自https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin 的XJC 插件並向 wsimport 添加 -B-Xsetter 參數

可以根據自己的特定要求編寫自己的XJC plugin

在這個例子中,一個人試圖在從xjc生成的每個 java 文件中添加一個long類型的id字段:

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/

它們是可用於在github 上生成hashCodeequalssetters-for-list插件。

參考:

回答我曾經問過的一個問題

如果有人在這里尋找一種方法來擺脫 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,我還使用org.jvnet.jaxb2_commons:jaxb2-basics-annotate和綁定文件將@lombok.Setter添加到某些類。 因此這些類最終成為標准 bean。

如果有人知道一種不那么笨拙的方式 - 例如,XJC 插件,我很樂意聽到它。

需要在插件內部添加依賴。 例如如下,

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM