web-dev-qa-db-ja.com

JAXBがリストのセッターを生成しない理由

XSDからJAXBクラスを生成すると、maxOccurs="unbounded"は、次のように生成されたゲッターメソッドを取得しますが、セッターメソッドは取得しません。

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

メソッドのコメントにより、どのように使用できるかが明確になりますが、私の質問は次のとおりです。
Java Beansルール?)に従ってJAXBがセッターを生成しないのはなぜですか?セッターメソッドを自分で記述できることはわかっていますが、生成されたゲッターメソッドで提案されているアプローチの利点はありますか?

これは私の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>
55
Ahmad Y. Saleh

JAXB仕様の正当化は60ページです。

設計上の注意– Listプロパティにはセッターメソッドはありません。ゲッターは参照によりリストを返します。 Java.util.Listで定義された適切なメソッドを使用して、getterメソッドによって返されるリストにアイテムを追加できます。 JAXB 1.0でのこの設計の理由は、実装がリストをラッピングし、コンテンツがリストに追加または削除されたときにチェックを実行できるようにすることでした。

そのため、リストの実装が検証を実行するためにadd/removeをオーバーライドしていた場合、その「特別な」リストを(たとえば)ArrayListに置き換えると、これらのチェックが無効になります。

27
jdessey

リンク: リストのセッターなし

Getterメソッドのコードにより、リストが作成されます。対応するsetterはありません。つまり、リスト要素のすべての追加または削除は、 "liveで行う必要があります。 「リスト。

Getterメソッドを使用するときのようにセッターは存在しないと引用が示しているように、リストの新しいインスタンスが存在しない場合は初期化されます。

その後、何かを追加または削除する必要がある場合は、使用する必要があります

getElement3().add(Type);

[〜#〜] update [〜#〜]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/>

リストがemptyの例

@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>
25
Narendra Pathai

上記のパトリックの懸念に同意します。生成されたJavaクラスに直接コーディングしていたなら、喜んで従うでしょうが、セッターまたは直接アクセス可能なメンバーを期待する内省的なツールを使用しています。プラグインを使用して成功しました https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin からXJCへ、およびwsimportに-B-Xsetter引数を追加

4

特定の要件に応じて、独自のXJC pluginを作成できます。

この例では、すべての生成されたJava idからのファイルに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;
    }

}

完全な例 こちら

ここの別の例:

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

これらは、 githubhashCodeequalssetters-for-listを生成するために利用できるプラグインです。

参照:

質問 への回答.

2

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プラグインを知っているなら、それを聞きたいです。

1
Lucas Ross