2012-04-20 8 views
22

Ho due codici, in due diversi progetti java, che eseguono quasi la stessa cosa, (annullando il richiamo dell'input di un webservice in base a un file xsd).quando JAXB unmarshaller.unmarshal restituisce un JAXBElement <MySchemaObject> o un MySchemaObject?

Ma in un caso dovrei scrivere questo: (Input è un nome segnaposto) (elemento è entrata OMElement)

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

e nell'altra lib devo usare JAXBElement.getValue(), perché è un JAXBElement che viene restituito, e una semplice (Input) gettano semplicemente si blocca:

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

sai cosa porta ad una tale differenza?

+0

penso guardando XSD ha un senso perché dipende dal fatto che si unmarshalling ad un SimpleType o ComplexType. – Phani

risposta

19

Se l'elemento principale corrisponde univocamente ad una classe Java quindi un'istanza di tale classe sarà restituito, e se non verrà restituito un JAXBElement.

Se si desidera assicurarsi di ottenere sempre un'istanza dell'oggetto dominio, è possibile utilizzare lo JAXBInstrospector. Di seguito è un esempio.

Demo

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

} 

uscita

class forum10243679.Root 
class forum10243679.Root 
class javax.xml.bind.JAXBElement 
class forum10243679.Root 
+1

Per curiosità, perché si dovrebbe assegnare un nome al pacchetto 'forum10243679'? – Saintali

+11

@Saintali - Provo a fornire un esempio di codice funzionante con ciascuna delle mie risposte. Il nome del pacchetto mi aiuta a trovare il codice di esempio se ho bisogno di rivederlo. Ho usato il prefisso 'forum' per esempi di codice che corrispondono a forum pubblici come Stack Overflow (utilizzo il prefisso bug per ricreare bug) e il numero corrisponde all'ID domanda che può essere trovato nell'URL. –

1

è necessario aggiungere alla vostra classe generata JAXB corretta @XMLRootElement - dovrebbe avere spazio dei nomi:

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

Dai un'occhiata alla la relativa domanda (ci sono un sacco di buoni consigli): Class Cast Exception when trying to unmarshall xml?

+2

Nota: lo spazio dei nomi può anche essere specificato con un pacchetto di annotazione @ javax.xml.bind.annotation.XmlSchema (di solito in package-info.java) – Puce

4

Dipende dalla presenza di XmlRootElement annotation nella classe dell'elemento radice.

Se si genera le vostre classi JAXB da un XSD, le seguenti regole vengono applicate:

  • se il tipo di l'elemento principale è un tipo anonimo -> XmlRootElement annotazione viene aggiunta alla classe generata
  • se il tipo dell'elemento root è un tipo di livello superiore -> annotazione XmlRootElement viene omessa dalla classe generata

Per tale motivo, spesso scelgo i tipi anonimi per gli elementi radice.

È possibile personalizzare il nome classe di questo tipo anonimo con un file di personalizzazione. Per esempio. creare un file bindings.xjc in questo modo:

<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> 
0

Modificare le classi java generate, non sono d'accordo. Non permettendo tutto il possibile formato xsd, non sono d'accordo.

Grazie a tutte le vostre spiegazioni e collegamenti, questo è il codice che ho scritto in modo da occuparmi di entrambi i casi, utilizzando Annotation Introspection. Funziona per la produzione che come input, ed è (nel mio gusto) più generico:

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

Si dovrebbe essere in grado di sfruttare la classe 'JAXBIntrospector' per semplificare il codice. Ho aggiunto una risposta: http://stackoverflow.com/a/10253282/383861 –

0

Ho lo stesso problema. JAXB unmarshaller.unmarshal restituisce un JAXBElement<MyObject> anziché il desiderato MyObject.

ho trovato e rimosso @XmlElementDecl. Il problema è risolto.

Problemi correlati