Domanda

Sto provando a generare classi Java dalla versione 4.5 di FpML (Finanial Products Markup Language). Viene generata una tonnellata di codice, ma non posso usarlo. Cercando di serializzare un documento semplice ottengo questo:

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]

In effetti i no corsi hanno l'annotazione @XmlRootElement, quindi cosa posso fare di sbagliato ?. Sto indicando xjc (JAXB 2.1) a fpml-main-4-5.xsd, che quindi include tutti i tipi.

È stato utile?

Soluzione

Per collegare ciò che altri hanno già affermato o accennato, le regole con cui JAXB XJC decide se inserire o meno l'annotazione @XmlRootElement su una classe generata non sono banali ( leggi questo articolo ).

@XmlRootElement esiste perché il runtime JAXB richiede determinate informazioni per eseguire il marshalling / unmarshal di un determinato oggetto, in particolare il nome dell'elemento XML e lo spazio dei nomi. Non puoi semplicemente passare qualsiasi vecchio oggetto al Marshaller. @XmlRootElement fornisce queste informazioni.

L'annotazione è solo una comodità, tuttavia - JAXB non lo richiede. L'alternativa è usare gli oggetti wrapper JAXBElement , che forniscono le stesse informazioni di @XmlRootElement , ma sotto forma di un oggetto, piuttosto che un'annotazione.

Tuttavia, gli oggetti JAXBElement sono scomodi da costruire, dal momento che è necessario conoscere il nome dell'elemento XML e lo spazio dei nomi, cosa che la logica aziendale di solito non riesce.

Per fortuna, quando XJC genera un modello di classe, genera anche una classe chiamata ObjectFactory . Questo è in parte lì per la retrocompatibilità con JAXB v1, ma è anche un posto dove XJC può mettere metodi di fabbrica generati che creano involucri JAXBElement attorno ai tuoi oggetti. Gestisce il nome XML e lo spazio dei nomi per te, quindi non devi preoccuparti. Devi solo cercare i metodi ObjectFactory (e per schemi di grandi dimensioni, ce ne possono essere centinaia) per trovare quello che ti serve.

Altri suggerimenti

Questo è menzionato in fondo al post del blog già linkato sopra, ma per me funziona come un piacere:

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

Come accennato in una delle risposte di cui sopra, non si otterrà un XMLRootElement sull'elemento root se nell'XSD il suo tipo è definito come un tipo denominato, poiché quel tipo denominato potrebbe essere utilizzato altrove nell'XSD. Prova a impostarlo come un tipo anonimo, ovvero anziché:

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

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

avresti:

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

@XmlRootElement non è necessario per unmarshalling - se si utilizza il modulo a 2 parametri di Unmarshaller # unmarshall.

Quindi, se invece di fare:

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

uno dovrebbe fare:

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

Quest'ultimo codice non richiederà l'annotazione @XmlRootElement a livello di classe UserType.

La risposta di Joe (Joe, 26 giugno 2009 alle 17:26) lo fa per me. La semplice risposta è che l'assenza di un'annotazione @XmlRootElement non è un problema se si esegue il marshalling di un JAXBElement. La cosa che mi ha confuso è che l'ObjectFactory generato ha 2 metodi createMyRootElement: il primo non accetta parametri e fornisce l'oggetto da scartare, il secondo prende l'oggetto da scartare e lo restituisce racchiuso in un JAXBElement e il marshalling di JAXBElement funziona bene. Ecco il codice di base che ho usato (ne sono nuovo, quindi mi scuso se il codice non è stato formattato correttamente in questa risposta), in gran parte paralizzato da testo del link :

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

Puoi risolvere questo problema usando l'associazione di Come generare le classi @XmlRootElement per i tipi di base in XSD? .

Ecco un esempio con 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>

Ecco il contenuto del file 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>

Come sapete, la risposta è usare ObjectFactory (). Ecco un esempio del codice che ha funzionato per me :)

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

Non funziona neanche per noi. Ma abbiamo trovato un articolo ampiamente citato che aggiunge ALCUNI retroscena ... Lo collegherò qui per il bene della prossima persona: http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html

Nel caso in cui la mia esperienza di questo problema dia a qualcuno un Eureka! momento .. Aggiungerò quanto segue:

Stavo riscontrando anche questo problema, quando utilizzavo un file xsd che avevo generato usando IntelliJ "Genera xsd dal documento di istanza" opzione di menu.

Quando ho accettato tutte le impostazioni predefinite di questo strumento, ha generato un file xsd che, quando utilizzato con jaxb, ha generato file java senza @XmlRootElement . In fase di esecuzione quando ho provato a eseguire il marshalling ho ottenuto la stessa eccezione di quella discussa in questa domanda.

Sono tornato allo strumento IntellJ e ho visto l'opzione predefinita in " Desgin Type " drop down (che ovviamente non ho capito .. e ancora non lo faccio se sono onesto) era:

Tipo di progetto:

  

" elementi locali / tipi complessi globali "

L'ho cambiato in

  

" elementi / tipi locali "

, ora ha generato un xsd (sostanzialmente) diverso, che ha prodotto il @XmlRootElement quando usato con jaxb. Non posso dire di aver capito bene dentro e fuori, ma ha funzionato per me.

Con una build Maven, puoi aggiungere l'annotazione @XmlRootElement

con il " jaxb2-basics-annotate " plug-in.

Vedi più informazioni: vedi

Configura Maven per generare classi dallo schema XML usando JAXB

e Generazione di codice JAXB XJC

Hai provato a cambiare la tua xsd in questo modo?

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

I wrapper JAXBElement funzionano nei casi in cui non viene generato @XmlRootElement da JAXB. Questi wrapper sono disponibili nella classe ObjectFactory generata da maven-jaxb2-plugin . Ad esempio:

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

Dopo due giorni di ricerca ho trovato la soluzione al problema. Puoi usare la classe ObjectFactory per risolvere il problema per le classi che non hanno @XmlRootElement . ObjectFactory ha sovraccaricato i metodi per avvolgerlo attorno a JAXBElement. Metodo: 1 esegue la semplice creazione dell'oggetto e Metodo: 2 avvolgerà l'oggetto con @JAXBElement . Utilizzare sempre Metodo: 2 per evitare javax.xml.bind.MarshalException - con l'eccezione collegata mancante un'annotazione @XmlRootElement

Metodo: 1

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

Metodo: 2

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

Esempio di codice funzionante:

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

Per risolverlo, è necessario configurare un'associazione xml prima di compilare con wsimport, impostando generateElementProperty su 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>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top