Pourquoi JAXB ne génère-t-il pas des setters pour les listes?

quand je génère des classes JAXB à partir D'un XSD, les éléments avec maxOccurs="unbounded" reçoivent une méthode getter générée pour eux, mais aucune méthode setter, comme suit:

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

le commentaire de la méthode est très clair sur la façon dont je peux l'utiliser, mais ma question Est la suivante:

pourquoi JAXB ne génère-t-il pas simplement un setter, en suivant les règles de Java Beans? je sais que je peux écrire la méthode setter moi-même, mais est-il un avantage à l'approche suggérée dans la méthode getter générée?

C'est mon 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>
47
demandé sur Ahmad Y. Saleh 2012-12-17 15:12:02

5 réponses

Voici la justification tirée de la spécification JAXB - page 60.

note de conception – il n'y a pas de méthode de setter pour une propriété de liste. Le getter renvoie la liste par référence. Un élément peut être ajouté à la Liste retournée par la méthode getter en utilisant une méthode appropriée défini sur java.util.Liste. raison D'être de cette conception dans JAXB 1.0 était pour permettre à la mise en œuvre d'envelopper la liste et être en mesure de effectuer des vérifications de le contenu a été ajouté ou supprimé de la Liste.

ainsi, si la mise en œuvre de la liste devait supplanter add/remove pour effectuer la validation, remplacer cette liste "spéciale" par (par exemple) un ArrayList irait à l'encontre de ces vérifications.

23
répondu jdessey 2014-08-26 13:34:10

lien pour : aucun setter pour la liste

le code dans la méthode getter assure que la liste est créé. Il n'y a pas de setter ce qui signifie que tout les éléments de la liste doivent être ajoutés ou supprimés sur le "live liste.

comme la citation dit qu'il n'y a aucun setter comme quand vous utilisez la méthode getter il assure qu'une nouvelle instance de la liste est initialisée si pas présent.

Et après cela, lorsque vous devez ajouter ou enlever quoi que ce soit, vous devrez utiliser

getElement3().add(Type);

mise à JOUR : Différence en œuvre pour null et la liste vide

exemple où la liste est null

@XmlRootElement(name = "list-demo")
public class ListDemo {

    @XmlElementWrapper(name = "list")
    @XmlElement(name = "list-item")
    private List<String> list;

}

sortie sera

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo/>

exemple où la liste est vide

@XmlRootElement(name = "list-demo")
public class ListDemo {

    @XmlElementWrapper(name = "list")
    @XmlElement(name = "list-item")
    private List<String> list = new ArrayList<String>();

}

sortie sera:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<list-demo>
    <list/>
</list-demo>
24
répondu Narendra Pathai 2012-12-17 14:34:33

D'accord avec la préoccupation de Patrick ci-dessus. Si je codais directement vers les classes java générées, je serais heureux d'obliger, mais j'utilise un outil introspectif attend soit un setter ou un membre directement accessible. A eu du succès en utilisant un plugin à XJC de https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Setters-Plugin et l'ajout d'un argument a-B-Xsetter à wsimport

3
répondu Chris Klopfenstein 2016-10-14 01:19:40

dans le cas où quelqu'un est ici à la recherche d'un moyen de se débarrasser de ces initialiseurs ennuyeux paresseux dans le code généré par XJC... Dans my Maven POM, cela va sous <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>

pour les setters, j'ajoute aussi @lombok.Setter à certaines classes en utilisant org.jvnet.jaxb2_commons:jaxb2-basics-annotate et un fichier de reliures. Ainsi, les classes finissent par être des haricots standard.

j'aimerais l'entendre si quelqu'un connaît une façon moins hacky--par exemple, un plugin XJC.

1
répondu Lucas Ross 2017-11-25 09:30:40

on peut écrire leur propre XJC plugin pour leurs besoins spécifiques.

dans cet exemple, on essaie d'ajouter un id champ de type long dans chaque fichier java généré à partir de 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;
    }

}

exemple Complet ici .

un autre exemple ici:

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

leurs sont des plugins disponibles pour générer hashCode , equals , setters-for-list au github aussi.

, les Références:

réponse à une question j'avais demandé.

1
répondu Siddharth Trikha 2018-10-03 08:27:51