web-dev-qa-db-fra.com

quand JAXB unmarshaller.unmarshal renvoie-t-il un objet JAXBElement <MySchemaObject> ou un objet MySchemaObject?

J'ai deux codes, dans deux projets Java différents, qui font à peu près la même chose (ce qui permet de masquer l'entrée d'un service Web selon un fichier xsd).

Mais dans un cas, je devrais écrire ceci: (Entrée est un nom d’espace réservé) (élément est entrée OMElement)

ClassLoader clInput = input.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("input", clInput);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() );

et dans l'autre bibliothèque, je dois utiliser JAXBElement.getValue (), car c'est un JAXBElement qui est renvoyé, et une distribution simple (Input) se bloque simplement:

Input input = (Input)unmarshaller.unmarshal( element.getXMLStreamReader() ).getValue();

Savez-vous ce qui mène à une telle différence?

27
Stephane Rolland

Si l'élément racine correspond de manière unique à une classe Java, une instance de cette classe sera renvoyée et, dans le cas contraire, une JAXBElement sera renvoyée. 

Si vous voulez vous assurer que vous obtenez toujours une instance de l'objet de domaine, vous pouvez utiliser la variable JAXBInstrospector. Ci-dessous un exemple.

Démo

package forum10243679;

import Java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    private static final String XML = "<root/>";

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        JAXBIntrospector jaxbIntrospector = jc.createJAXBIntrospector();

        Object object = unmarshaller.unmarshal(new StringReader(XML));
        System.out.println(object.getClass());
        System.out.println(jaxbIntrospector.getValue(object).getClass());

        Object jaxbElement = unmarshaller.unmarshal(new StreamSource(new StringReader(XML)), Root.class);
        System.out.println(jaxbElement.getClass());
        System.out.println(jaxbIntrospector.getValue(jaxbElement).getClass());
    }

}

Sortie

class forum10243679.Root
class forum10243679.Root
class javax.xml.bind.JAXBElement
class forum10243679.Root
24
Blaise Doughan

Cela dépend de la présence de XmlRootElement annotation de la classe de votre élément racine.

Si vous générez vos classes JAXB à partir d'un fichier XSD, les règles suivantes sont appliquées:

  • si le type de l'élément racine est un type anonyme -> l'annotation XmlRootElement est ajoutée à la classe générée
  • si le type de l'élément racine est un type de niveau supérieur -> l'annotation XmlRootElement est omise de la classe générée

Pour cette raison, je choisis souvent des types anonymes pour les éléments racines.

Vous pouvez personnaliser le nom de classe de ce type anonyme avec un fichier de personnalisation. Par exemple. Créez un fichier bindings.xjc comme ceci:

<jxb:bindings version="1.0"
              xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb"
              xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="yourXsd.xsd" node="/xs:schema">
        <jxb:bindings  node="//xs:element[@name='yourRootElement']">
            <jxb:class name="YourRootElementType"/>
        </jxb:bindings> 
    </jxb:bindings>
</jxb:bindings>
4
Puce

Vous devez ajouter à votre classe générée par JAXB @XMLRootElement propre - elle doit avoir un espace de noms: 

@XmlRootElement(namespace="http://your.namespace.com/", name="yourRootElement")

Jetez un oeil à la question connexe (il y a beaucoup de bons conseils): Classe Cast Exception lorsqu’on essaie de désarmer XML?

1
Piotr Kochański

J'ai le même problème. JAXB unmarshaller.unmarshal renvoie un JAXBElement<MyObject> au lieu de MyObject souhaité. 

J'ai trouvé et supprimé @XmlElementDecl. Le problème est résolu.

0
test7788

Je ne suis pas d'accord avec la modification des classes Java générées… .. Ne permettant pas tous les formats xsd possibles, je ne suis pas d'accord.

Merci à toutes vos explications et liens, c’est le code que j’ai écrit afin de prendre en charge les deux cas en utilisant Annotation Introspection. Cela fonctionne aussi bien en sortie qu'en entrée, et est (à mon goût) plus générique:

public class JaxbWrapper {

    private static boolean isXmlRootElement(Class classT){

        Annotation[] annotations = classT.getAnnotations();

        for(Annotation annotation : annotations){
            if(annotation instanceof XmlRootElement){
                return true;
            }
        }       

        return false;
    }

    public static Object unmarshall(Class classObjectFactory, Class classObject, XMLStreamReader xmlStreamReader){

        Package pack = classObjectFactory.getPackage();
        String strPackageName = pack.getName();

        Object returnObject = null;

        try {
            JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());

            Unmarshaller unmarshaller = jc.createUnmarshaller();

            returnObject = unmarshaller.unmarshal( xmlStreamReader );

            boolean bIsRootedElement = isXmlRootElement(classObject);
            if(!bIsRootedElement)
            {
                JAXBElement jaxbElement = (JAXBElement) returnObject;
                returnObject = jaxbElement.getValue();              
            }
        }
        catch (JAXBException e) {
            /*...*/
        }   

        return returnObject;
    }

    private static void writeToXml(Class classObjectFactory, Object obj, XMLStreamWriter xmlStreamWriter){

        Package pack = classObjectFactory.getPackage();
        String strPackageName = pack.getName();

        try {       
            JAXBContext jc = JAXBContext.newInstance(strPackageName, classObjectFactory.getClassLoader());
            Marshaller marshaller = jc.createMarshaller();
            marshaller.marshal(obj, xmlStreamWriter);
        }
        catch(JAXBException e) {
            /*...*/
        }       
    }

    public static String marshall(Class classObjectFactory, Class classObject, Object obj){

        Object objectToMarshall = obj; 

        boolean bIsRootedElement = isXmlRootElement(classObject);
        if(!bIsRootedElement)
        {
            Package pack = classObjectFactory.getPackage();
            String strPackageName = pack.getName();

            String strClassName = classObject.getName();

            QName qName = new QName(strPackageName, strClassName);

            JAXBElement jaxbElement = new JAXBElement(qName, classObject, null, obj);

            objectToMarshall = jaxbElement; 
        }

        StringWriter sw = new StringWriter();
        XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
        XMLStreamWriter xmlStreamWriter = null;

        try {
            xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(sw);

            writeToXml(classObjectFactory, objectToMarshall, xmlStreamWriter);

            xmlStreamWriter.flush();
            xmlStreamWriter.close();
        } 
        catch (XMLStreamException e) {
            /*...*/
        }

        return sw.toString();
    }
}
0
Stephane Rolland