web-dev-qa-db-fra.com

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. En essayant de sérialiser un document simple, je reçois ceci:

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 no les classes ont l'annotation @XmlRootElement, alors que puis-je faire de mal ?. Je pointe xjc (JAXB 2.1) sur fpml-main-4-5.xsd, qui inclut alors tous les types.

197
robinr

Pour relier ce que d'autres ont déjà déclaré ou laissé entendre, les règles selon lesquelles JAXB XJC décide de mettre ou non l'annotation @XmlRootElement sur une classe générée ne sont pas triviales ( voir cet article ) .

@XmlRootElement existe parce que le moteur d'exécution JAXB requiert certaines informations afin de marshal/unmarshal un objet donné, en particulier le nom de l'élément XML et l'espace de nom. Vous ne pouvez pas simplement passer un vieil objet au Marshaller. @XmlRootElement fournit ces informations.

L'annotation n'est cependant qu'un avantage - JAXB n'en a pas besoin. L'alternative consiste à utiliser les objets wrapper JAXBElement, qui fournissent les mêmes informations que @XmlRootElement, mais sous la forme d'un objet plutôt que d'une annotation.

Cependant, les objets JAXBElement sont difficiles à construire, car vous devez connaître le nom de l'élément XML et son espace de nom, contrairement à la logique métier.

Heureusement, lorsque XJC génère un modèle de classe, il génère également une classe appelée ObjectFactory. C’est en partie là pour la compatibilité ascendante avec JAXB v1, mais c’est également un endroit où XJC peut mettre en place les méthodes de fabrique générées qui créent JAXBElement enveloppeurs autour de vos propres objets. Il gère le nom XML et l'espace de nom pour vous, vous n'avez donc pas à vous en soucier. Il vous suffit de parcourir les méthodes ObjectFactory (et pour les schémas volumineux, il peut y en avoir des centaines) pour trouver celle dont vous avez besoin.

254
skaffman

Ceci est mentionné au bas de l'article de blog déjà lié ci-dessus 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);
66
Gurnard

Comme indiqué dans l'une des réponses ci-dessus, vous n'obtiendrez pas 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 d’en 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>
47
Matthew Wise

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

Donc, si au lieu de faire:

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

il faut faire:

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

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

34
Sayantam

La réponse de Joe (Joe 26 juin 2009 à 17h26) le fait pour moi. La réponse simple est que l'absence d'annotation @XmlRootElement n'est pas un problème si vous marshalez un JAXBElement. Ce qui m'a dérouté, c'est que l'ObjectFactory généré a deux méthodes createMyRootElement - la première ne prend aucun paramètre et donne l'objet non enveloppé, la seconde prend l'objet non enveloppé et le renvoie dans un JAXBElement et indique que JAXBElement fonctionne correctement. Voici le code de base que j'ai utilisé (je suis nouveau dans ce domaine, donc excuses si le code n'est pas formaté correctement dans cette réponse), en grande partie copié de texte du lien :

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;
  }
}
20
Yaqoob

Vous pouvez résoudre ce problème en utilisant la liaison de Comment générer des classes @XmlRootElement pour les types de base dans XSD? .

Voici un exemple avec Maven

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxb2-maven-plugin</artifactId>
            <version>1.3.1</version>
            <executions>
                <execution>
                    <id>xjc</id>
                    <goals>
                        <goal>xjc</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <schemaDirectory>src/main/resources/xsd</schemaDirectory>
                <packageName>com.mycompany.schemas</packageName>
                <bindingFiles>bindings.xjb</bindingFiles>
                <extension>true</extension>
            </configuration>
        </plugin>

Voici le contenu du fichier binding.xjb

<?xml version="1.0"?>
<jxb:bindings version="1.0" xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb"
              xmlns:xjc= "http://Java.Sun.com/xml/ns/jaxb/xjc"
              jxb:extensionBindingPrefixes="xjc" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="path/to/myschema.xsd" node="/xs:schema">
        <jxb:globalBindings>
            <xjc:simple/>
        </jxb:globalBindings>
    </jxb:bindings>
</jxb:bindings>
17
Olivier.Roger

Comme vous le savez, la réponse consiste à utiliser ObjectFactory (). Voici un exemple du code qui a fonctionné pour moi :)

ObjectFactory myRootFactory = new ObjectFactory();

MyRootType myRootType = myRootFactory.createMyRootType();

try {

        File file = new File("./file.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(MyRoot.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        //output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        JABXElement<MyRootType> myRootElement = myRootFactory.createMyRoot(myRootType);

        jaxbMarshaller.marshal(myRootElement, file);
        jaxbMarshaller.marshal(myRootElement, System.out);

    } catch (JAXBException e) {
        e.printStackTrace();
    }
9
Shehaaz

Cela ne fonctionne pas pour nous non plus. Mais nous avons trouvé un article cité qui ajoute un peu d’arrière-plan ... Je vais faire un lien ici dans l’intérêt de la personne suivante: http://weblogs.Java.net/blog/kohsuke/archive /2006/03/why_does_jaxb_p.html

6
mcherm

Au cas où mon expérience de ce problème donne à quelqu'un un Eureka! moment .. Je vais ajouter ce qui suit:

J'avais également ce problème lorsque j'utilisais un fichier xsd que j'avais généré à l'aide du menu "Générer xsd à partir d'un document d'instance" de IntelliJ.

Lorsque j'ai accepté tous les paramètres par défaut de cet outil, il a généré un fichier xsd qui, lorsqu'il était utilisé avec jaxb, générait des fichiers Java sans @XmlRootElement. Au moment de l'exécution, lorsque j'ai essayé de rassembler, j'ai eu la même exception que celle décrite dans cette question.

Je suis retourné à l'outil IntellJ et j'ai vu l'option par défaut dans la liste déroulante "Type de description" (que je ne comprenais bien sûr pas ... et que je ne comprends toujours pas si je suis honnête):

Type de Desgin:

"éléments locaux/types complexes globaux"

J'ai changé cela en

"éléments/types locaux"

, à présent, il générait un xsd (sensiblement) différent, qui produisait le @XmlRootElement lorsqu’il était utilisé avec jaxb. Je ne peux pas dire que je comprends les entrées et les sorties, mais cela a fonctionné pour moi.

5
johnm

Avec une construction Maven, vous pouvez ajouter l’annotation @XmlRootElement

avec le plug-in "jaxb2-basics-annotate".

Voir plus d'informations: voir

Configurer Maven pour générer des classes à partir d'un schéma XML à l'aide de JAXB

et génération de code JAXB XJC

4
metatechbe

Les wrappers JAXBElement fonctionnent dans les cas où aucun @XmlRootElement n'est généré par JAXB. Ces wrappers sont disponibles dans la classe ObjectFactory générée par maven-jaxb2-plugin. Pour par exemple:

     public class HelloWorldEndpoint {
        @PayloadRoot(namespace = NAMESPACE_URI, localPart = "person")
        @ResponsePayload
        public JAXBElement<Greeting> sayHello(@RequestPayload JAXBElement<Person> request) {

        Person person = request.getValue();

        String greeting = "Hello " + person.getFirstName() + " " + person.getLastName() + "!";

        Greeting greet = new Greeting();
        greet.setGreeting(greeting);

        ObjectFactory factory = new ObjectFactory();
        JAXBElement<Greeting> response = factory.createGreeting(greet);
        return response;
      }
 }
3
zer0Id0l

Avez-vous essayé de changer votre xsd comme ça?

<!-- create-logical-system -->
<xs:element name="methodCall">
  <xs:complexType>
    ...
  </xs:complexType>
</xs:element>
3
Tony

Au bout de deux jours, j'ai trouvé la solution au problème. Vous pouvez utiliser la classe ObjectFactory pour la solution de contournement des classes qui ne possèdent pas le @ XmlRootElement. ObjectFactory a surchargé les méthodes pour l'enrouler autour de JAXBElement.

Méthode: 1 effectue la création simple de l'objet.

Méthode: 2 va envelopper l'objet avec @ JAXBElement.

Utilisez toujours Méthode: 2 pour éviter javax.xml.bind.MarshalException - avec une exception liée manquant d’une annotation @XmlRootElement.

Veuillez trouver l'exemple de code ci-dessous

Méthode: 1 effectue la création simple de l'objet

public GetCountry createGetCountry() {
        return new GetCountry();
    }

Méthode: 2 va envelopper l'objet avec @ JAXBElement.

 @XmlElementDecl(namespace = "my/name/space", name = "getCountry")
 public JAXBElement<GetCountry> createGetCountry(GetCountry value) {
        return new JAXBElement<GetCountry>(_GetCountry_QNAME, GetCountry.class, null, value);
    }

Exemple de code de travail:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
WebServiceTemplate springWSTemplate = context.getBean(WebServiceTemplate.class);

GetCountry request = new GetCountry();
request.setGuid("test_guid");

JAXBElement<GetCountryResponse> jaxbResponse = (JAXBElement<GetCountryResponse>)springWSTemplate .marshalSendAndReceive(new ObjectFactory().createGetCountry(request));

GetCountryResponse response = jaxbResponse.getValue();
3
prasadg

Pour le résoudre, vous devez configurer une liaison XML avant de compiler avec wsimport, en définissant generateElementProperty sur false.

     <jaxws:bindings wsdlLocation="LOCATION_OF_WSDL"
      xmlns:jaxws="http://Java.Sun.com/xml/ns/jaxws"
      xmlns:xjc="http://Java.Sun.com/xml/ns/jaxb/xjc" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb"
      xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
         <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
    <jaxws:bindings  node="wsdl:definitions/wsdl:types/xs:schema[@targetNamespace='NAMESPACE_OF_WSDL']">
      <jxb:globalBindings xmlns:jxb="http://Java.Sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xjc:generateElementProperty>false</xjc:generateElementProperty> 
      </jxb:globalBindings>
  </jaxws:bindings>
</jaxws:bindings>
1
leandruol