2012-04-20 10 views
22

Ich habe zwei Codes, in zwei verschiedenen Java-Projekten, die fast die gleiche Sache machen (die Eingabe eines Webservice nach einer xsd-Datei abmarshalieren).wann gibt JAXB unmarshaller.unmarshal ein JAXBElement <MySchemaObject> oder ein MySchemaObject zurück?

Aber in einem Fall sollte ich schreiben: (Input ist ein Platzhalter Name) (Element ist OMElement Eingang)

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

und in der anderen lib muss ich JAXBElement.getValue(), weil es ein JAXBElement, der zurückgegeben wird, und eine einfache (Input) werfen einfach abstürzt:

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

wissen Sie, was zu einer solchen Differenz führt?

+0

Ich denke, XSD macht Sinn, weil es davon abhängt, ob Sie zu einem SimpleType oder ComplexType entpacken. – Phani

Antwort

19

Wenn das Wurzelelement zu einer Java-Klasse eindeutig entspricht dann eine Instanz dieser Klasse wird zurückgegeben, und wenn kein JAXBElement wird zurückgegeben. Wenn Sie sicherstellen möchten, dass Sie immer eine Instanz des Domänenobjekts erhalten, können Sie die JAXBInstrospector nutzen. Unten ist ein Beispiel.

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

} 

Ausgabe

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

Aus Neugier, warum würden Sie Ihr Paket 'forum10243679' nennen? – Saintali

+11

@Saintali - Ich versuche, mit jeder meiner Antworten ein funktionierendes Codebeispiel zu erstellen. Der Paketname hilft mir, das Codebeispiel zu finden, wenn ich es erneut aufrufen muss. Ich habe das Präfix 'forum' für Code-Beispiele verwendet, die öffentlichen Foren wie Stack Overflow entsprechen (Ich benutze das Bug-Präfix für die Neuerstellung von Fehlern), und die Nummer entspricht der Frage-ID, die in der URL gefunden werden kann. –

1

Sie müssen Ihre JAXB generierten Klasse hinzuzufügen richtigen @XMLRootElement - es Namensraum haben sollte:

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

Werfen Sie einen Blick auf die damit verbundene Frage (es gibt viele gute Tipps): Class Cast Exception when trying to unmarshall xml?

+2

Hinweis: Der Namespace kann auch mit einer @ javax.xml.bind.annotation.XmlSchema-Paketannotation angegeben werden (normalerweise in package-info.java) – Puce

4

Es hängt von der Anwesenheit von XmlRootElement annotation für die Klasse Ihres Root-Elements ab.

Wenn Sie Ihre JAXB-Klassen aus einem XSD generieren, werden die folgenden Regeln angewandt:

  • , wenn der Typ des Root-Elements ein anonymer Typ ist -> XmlRootElement Anmerkung zu der generierten Klasse hinzugefügt wird
  • Wenn der Typ des Wurzelelements ein Top-Level-Typ ist -> XmlRootElement-Annotation ist aus der generierten Klasse

weggelassen. Aus diesem Grund wähle ich oft anonyme Typen für Root-Elemente.

Sie können den Klassennamen dieses anonymen Typs mit einer Anpassungsdatei anpassen. Z.B. erstellen bindings.xjc-Datei wie folgt:

<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

Ändern erzeugten Java-Klassen, bin ich nicht einverstanden. Nicht alle möglichen xsd-Format zulassen, stimme ich nicht zu.

Danke an alle Ihre Erklärungen und Links, dies ist der Code, den ich geschrieben habe, um beide Fälle zu behandeln, mit Annotation Introspection. Es funktioniert für die Ausgabe sowie Ein- und ist (in meinem Geschmack) mehr generic:

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

Sie sollten die 'JAXBIntrospector'-Klasse verwenden können, um Ihren Code zu vereinfachen. Ich habe eine Antwort hinzugefügt: http://stackoverflow.com/a/10253282/383861 –

0

Ich habe das gleiche Problem. JAXB unmarshaller.unmarshal gibt eine JAXBElement<MyObject> statt der gewünschten MyObject zurück.

Ich habe gefunden und entfernt @XmlElementDecl. Das Problem ist behoben.