Program Tip

JAXB가 목록에 대한 setter를 생성하지 않는 이유

programtip 2021. 1. 8. 22:13
반응형

JAXB가 목록에 대한 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>

다음은 JAXB 사양의 정당성입니다-60 페이지.

디자인 노트 – List 속성에 대한 setter 메서드는 없습니다. getter는 참조로 List를 반환합니다. java.util.List에 정의 된 적절한 메소드를 사용하여 getter 메소드가 리턴 한 List에 항목을 추가 할 수 있습니다. JAXB 1.0에서이 디자인의 근거는 구현이 목록을 래퍼하고 목록에서 콘텐츠가 추가되거나 제거 될 때 검사를 수행 할 수 있도록하는 것입니다.

따라서 목록의 구현이 유효성 검사를 수행하기 위해 추가 / 제거를 재정의하는 경우 해당 '특별한'목록을 (예를 들어) ArrayList로 대체하면 이러한 검사가 무효화됩니다.


링크 : 목록에 대한 setter 없음

getter 메서드의 코드는 List가 생성되었는지 확인합니다. 해당하는 setter 가 없으므로 목록 요소의 모든 추가 또는 삭제는 "라이브" 목록 에서 이루어져야 합니다.

따옴표에서 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>

위의 Patrick의 우려에 동의하십시오. 생성 된 자바 클래스에 직접 코딩하는 경우 기꺼이 의무를 지지만 내성 도구를 사용하고 있는데 setter 또는 직접 액세스 가능한 멤버를 기대합니다. https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin 에서 XJC에 대한 플러그인을 사용하고 wsimport에 -B-Xsetter 인수를 추가하는 데 성공했습니다.


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/

Their are plugins available for generating hashCode, equals, setters-for-list at github too.

References:

Answer to a question I had asked.


In case anyone is here looking for a way to get rid of those annoying lazy initializers in XJC-generated code... In my Maven POM, this goes under <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>

For setters, I'm also adding @lombok.Setter to certain classes using org.jvnet.jaxb2_commons:jaxb2-basics-annotate and a bindings file. Thus the classes end up being standard beans.

I would love to hear it if anyone knows of a less hacky way--e.g., a XJC plugin.

ReferenceURL : https://stackoverflow.com/questions/13913000/why-doesnt-jaxb-generate-setters-for-lists

반응형