Question

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

Était-ce utile?

La solution

Pour lier ce que d'autres ont déjà indiqué ou laissé entendre, les règles selon lesquelles JAXB XJC décide de placer 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 de JAXB requiert certaines informations afin de marshal / unarsar 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’une commodité - JAXB n’en a pas besoin. L'alternative consiste à utiliser des 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 aussi un endroit où XJC peut placer les méthodes de fabrique générées qui créent des enveloppes JAXBElement 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.

Autres conseils

Ceci est mentionné au bas de l'article de blog déjà lié ci-dessus, mais cela fonctionne comme une friandise 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);

Comme indiqué dans l'une des réponses ci-dessus, vous n'obtiendrez pas XMLRootElement sur votre élément racine si son type est défini dans le XSD comme un 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>

vous auriez:

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

@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.

La réponse de Joe (Joe le 26 juin 09 à 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 2 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 des excuses si le code n'est pas formaté correctement dans cette réponse), en grande partie depuis 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;
  }
}

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>

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();
    }

Cela ne fonctionne pas pour nous non plus. Mais nous avons trouvé un article largement cité qui ajoute un peu de contexte ... Je vais vous le lier ici pour le plaisir de la personne suivante: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

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

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

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, j’ai eu la même exception que celle décrite dans cette question.

Je suis revenu sur l'outil IntellJ et j'ai vu l'option par défaut dans le type "Desgin Type". descendre (que je n'ai bien sûr pas compris… et que je ne comprends toujours pas si je suis honnête) était:

Type de document:

  

"Éléments locaux / Types complexes globaux"

J'ai changé cela en

  

"Eléments / types locaux"

, il génère maintenant un xsd (sensiblement) différent, qui produit le @XmlRootElement lorsqu’il est utilisé avec jaxb. Je ne peux pas dire que je comprends les entrées et les sorties, mais cela a fonctionné pour moi.

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

.

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

Voir plus d'informations: voir

Configurer Maven pour générer des classes à partir de XML Schema à l'aide de JAXB

et Génération de code JAXB XJC

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

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

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 . 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;
      }
 }

Après deux jours de lutte, j'ai trouvé la solution au problème. Vous pouvez utiliser la classe ObjectFactory pour résoudre le problème des classes ne disposant pas de @XmlRootElement . ObjectFactory a surchargé les méthodes pour l'enrouler autour de JAXBElement. Méthode: 1 effectue la création simple de l'objet et la Méthode: 2 encapsule l'objet avec @JAXBElement . Toujours utiliser la méthode: 2 pour éviter javax.xml.bind.MarshalException - avec une exception liée manquant d'une annotation @XmlRootElement

Méthode: 1

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

Méthode: 2

 @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("1f3e1771-3049-49f5-95e6-dc3732c3227b");

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

GetCountryResponse response = jaxbResponse.getValue();

Pour résoudre ce problème, 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>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top