interfaz - jframe en java
¿Por qué JAXB no genera setters para listas? (5)
Cuando genero clases JAXB desde un XSD, los elementos con maxOccurs="unbounded"
obtienen un método getter generado para ellos, pero no un método setter, de la siguiente manera:
/**
* 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;
}
El comentario del método lo deja muy claro sobre cómo puedo usarlo, pero mi pregunta es la siguiente:
¿Por qué JAXB no genera un setter, siguiendo las reglas de Java Beans? Sé que puedo escribir el método de establecimiento yo mismo, pero ¿hay alguna ventaja en el enfoque sugerido en el método de obtención generado?
Este es mi 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>
Aquí está la justificación de la especificación JAXB - página 60.
Nota de diseño: no existe un método de establecimiento para una propiedad de lista. El getter devuelve la lista por referencia. Se puede agregar un elemento a la Lista devuelta por el método getter usando un método apropiado definido en java.util.List. La razón de este diseño en JAXB 1.0 fue permitir que la implementación envolviera la lista y pudiera realizar comprobaciones a medida que el contenido se agregaba o eliminaba de la Lista.
Por lo tanto, si la implementación de la Lista fuera sobrepasar agregar / eliminar para realizar la validación, reemplazar esa Lista ''especial'' con (por ejemplo) una ArrayList anularía estas comprobaciones.
De acuerdo con la preocupación de Patrick arriba. Si estuviera codificando las clases Java generadas directamente, estaría encantado de hacerlo, pero estoy usando una herramienta introspectiva que espera un establecedor o un miembro directamente accesible. Tuvo éxito usando un complemento para XJC desde https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin y agregando un argumento -B-Xsetter a wsimport
En caso de que alguien esté aquí buscando una manera de deshacerse de esos molestos inicializadores perezosos en el código generado por XJC ... En mi Maven POM, esto se incluye en <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>
Para los configuradores, también estoy agregando @lombok.Setter
a ciertas clases usando org.jvnet.jaxb2_commons:jaxb2-basics-annotate
y un archivo de enlaces. Así las clases terminan siendo frijoles estándar.
Me encantaría escucharlo si alguien sabe de una manera menos pirata, por ejemplo, un complemento XJC.
Enlace para: Sin setter para lista
El código en el método getter asegura que se crea la Lista. No hay un setter correspondiente, lo que significa que todas las adiciones o eliminaciones de elementos de la lista deben realizarse en la lista "en vivo" .
Como la cita dice que no hay un definidor, ya que cuando se usa el método de obtención, se asegura que una nueva instancia de la lista se inicialice si no está presente.
Y después de eso, cuando tenga que agregar o quitar algo, tendrá que usar
getElement3().add(Type);
ACTUALIZACIÓN : diferencia en ordenamiento para null
y lista vacía
Ejemplo donde la lista es null
@XmlRootElement(name = "list-demo")
public class ListDemo {
@XmlElementWrapper(name = "list")
@XmlElement(name = "list-item")
private List<String> list;
}
SALIDA será
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo/>
Ejemplo donde la lista está vacía
@XmlRootElement(name = "list-demo")
public class ListDemo {
@XmlElementWrapper(name = "list")
@XmlElement(name = "list-item")
private List<String> list = new ArrayList<String>();
}
SALIDA será:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo>
<list/>
</list-demo>
Uno puede escribir su propio XJC plugin
para sus requisitos específicos.
En este ejemplo, uno está intentando agregar un campo de id
de tipo long
en cada archivo java generado desde 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;
}
}
Ejemplo completo here .
Otro ejemplo aquí:
Hay complementos disponibles para generar hashCode
, equals
, setters-for-list
en github también.
Referencias:
Responde a una question te había pedido.