当我从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>
答案 0 :(得分:26)
以下是JAXB规范的理由 - 第60页。
设计说明 - List属性没有setter方法。该 getter通过引用返回List。可以将项目添加到 getter方法使用适当的方法返回的列表 在java.util.List上定义。 JAXB 1.0中此设计的基本原理是 使实现能够包装列表并能够 在列表中添加或删除内容时执行检查。
因此,如果List的实现覆盖了add / remove来执行验证,那么用(例如)ArrayList替换'special'List会使这些检查失败。
答案 1 :(得分:24)
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。
这里还有一个例子:
它们也是可以在github生成hashCode
,equals
,setters-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<[\w_]+>\(\);\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>