232 votes

Pas de @XmlRootElement généré par JAXB

J'essaie de générer des classes Java à partir de la version 4.5 de FpML (Finanial Products Markup Language). Une tonne de code est générée, mais je ne peux pas l'utiliser. J'essaie de sérialiser un document simple:

 javax.xml.bind.MarshalException
  - with linked exception: [com.sun.istack.SAXException2: unable
  to marshal type
  "org.fpml._2008.fpml_4_5.PositionReport"
  as an element because it is missing an
  @XmlRootElement annotation]
 

En fait, aucune classe n'a l'annotation @XmlRootElement, alors que puis-je faire de mal? Je pointe xjc (JAXB 2.1) vers fpml-main-4-5.xsd, qui inclut alors tous les types.

278voto

skaffman Points 197885

Pour lier ce que les autres ont déjà dit ou fait allusion à l', les règles par lesquelles JAXB XJC décide ou non de mettre la @XmlRootElement annotation sur une classe générée sont non trivial (voir cet article).

@XmlRootElement existe parce que la JAXB runtime exige que certaines informations afin de maréchal/unmarshal un objet donné, plus précisément le nom de l'élément XML et l'espace de noms. Vous ne pouvez pas vous passer de tout objet ancien pour le Marshaller. @XmlRootElement fournit cette information.

L'annotation est juste une commodité, cependant - JAXB ne le nécessite pas. L'alternative est d'utiliser JAXBElement wrapper objets, qui fournissent les mêmes informations que l' @XmlRootElement, mais sous la forme d'un objet, plutôt que d'une annotation.

Toutefois, JAXBElement des objets sont difficiles à construire, car vous avez besoin de connaître le nom de l'élément XML et l'espace de noms, dont la logique d'entreprise n'est généralement pas le cas.

Heureusement, quand XJC génère un modèle de classe, il génère aussi une classe appelée ObjectFactory. C'est en partie là pour assurer la compatibilité ascendante avec JAXB v1, mais c'est aussi là que d'un endroit pour XJC de mettre généré usine méthodes qui créent JAXBElement wrappers autour de vos propres objets. Il gère le XML, le nom et l'espace de noms pour vous, donc vous n'avez pas besoin de s'inquiéter à ce sujet. Vous avez juste besoin de regarder à travers l' ObjectFactory méthodes (et pour les grands schéma, il peut y avoir des centaines d'entre eux) pour trouver celui dont vous avez besoin.

71voto

Gurnard Points 661

Ceci est mentionné au bas de l'article déjà lié au blog mais cela fonctionne comme un régal pour moi:

 Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(new JAXBElement<MyClass>(new QName("uri","local"), MyClass.class, myClassInstance), System.out);
 

59voto

Matthew Wise Points 221

Comme indiqué dans l'une des réponses ci-dessus, vous n'obtiendrez pas un XMLRootElement sur votre élément racine si, dans le XSD, son type est défini en tant que type nommé, car ce type nommé pourrait être utilisé ailleurs dans votre XSD. Essayez de lui faire un type anonyme, c’est-à-dire au lieu de:

 <xsd:element name="myRootElement" type="MyRootElementType" />

<xsd:complexType name="MyRootElementType">
...
</xsd:complexType>
 

tu aurais:

 <xsd:element name="myRootElement">
    <xsd:complexType>
    ...
    <xsd:complexType>
</xsd:element>
 

40voto

Sayantam Points 366

@XmlRootElement n'est pas nécessaire pour unmarshalling - si l'on utilise la forme à 2 éléments de Unmarshaller # unmarshall.

Donc, si au lieu de faire:

 UserType user = (UserType) unmarshaller.unmarshall(new StringReader(responseString));
 

on devrait faire:

 JAXBElement<UserType> userElement = unmarshaller.unmarshall(someSource, UserType.class);
UserType user = userElement.getValue();
 

Ce dernier code ne nécessitera pas d'annotation @XmlRootElement au niveau de la classe UserType.

21voto

Yaqoob Points 151

La réponse de Joe (Joe Jun 26 '09 à 17:26) le fait pour moi. La réponse simple est que l'absence d'un @XmlRootElement annotation n'est pas un problème si vous avez un maréchal JAXBElement. La chose qui me troublait est générée ObjectFactory a 2 createMyRootElement méthodes - la première prend pas de paramètres et donne la déballé l'objet, le second prend la déballé objet et le retourne enveloppé dans un JAXBElement, et de l'ordonnancement que JAXBElement fonctionne très bien. Voici le code de base que j'ai utilisé (je suis nouveau à cela, alors, toutes mes excuses si le code n'est pas correctement mis en forme dans cette réponse), en grande partie chipé par lien texte:

ObjectFactory objFactory = new ObjectFactory();
MyRootElement root = objFactory.createMyRootElement();
...
// Set root properties
...
if (!writeDocument(objFactory.createMyRootElement(root), output)) {
    System.err.println("Failed to marshal XML document");
}
...

private boolean writeDocument(JAXBElement document, OutputStream output) {

  Class<?> clazz = document.getValue().getClass();
  try {
    JAXBContext context =
        JAXBContext.newInstance(clazz.getPackage().getName());
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.marshal(document, output);
    return true;

  } catch (JAXBException e) {
    e.printStackTrace(System.err);
    return false;
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X