2011-06-26 20 views
9

estoy usando el parcial unmarshalling ejemplo de JAXB, pero no soy capaz de unmarshal XML elementos que no están en el nivel de raíz (porque ellos no tienen una etiqueta @XmlRootElement). En mi ejemplo, traté de leer Ship-Element en lugar de purchaseOrder-Element.JAXB elementos parciales-unmarshalling sin @XmlRootElement

Normalmente, trabajaría con JAXBElement unmarshal (Fuente de origen, Clase declarada Tipo) pero como el ejemplo está usando UnmarshallerHandler y XMLFilterImpl No sé dónde decirle a Jaxb qué Clase debería usar.

Mi mensaje de error es: Causado por: javax.xml.bind.UnmarshalException: inesperado elemento (uri: "", local: "shipTo"). elementos previstos son < {} el comentario>, < {} hojaPedido>, < {} PurchaseOrders>

I Googled alrededor de un lote, pero no encontró nada útil aún.

Aquí es el código de ejemplo de la jaxb-Página Web:

Main.java

public class Main { 
public static void main(String[] args) throws Exception { 

    // create JAXBContext for the primer.xsd 
    JAXBContext context = JAXBContext.newInstance("primer"); 

    // create a new XML parser 
    SAXParserFactory factory = SAXParserFactory.newInstance(); 
    factory.setNamespaceAware(true); 
    XMLReader reader = factory.newSAXParser().getXMLReader(); 

    // prepare a Splitter 
    Splitter splitter = new Splitter(context); 

    // connect two components 
    reader.setContentHandler(splitter); 

    for(int i=0; i<args.length; i++) { 
     // parse all the documents specified via the command line. 
     // note that XMLReader expects an URL, not a file name. 
     // so we need conversion. 
     reader.parse(new File(args[i]).toURL().toExternalForm()); 
    } 
} 

}

Splitter.java

public class Splitter extends XMLFilterImpl { 

public Splitter(JAXBContext context) { 
    this.context = context; 
} 

/** 
* We will create unmarshallers from this context. 
*/ 
private final JAXBContext context; 


public void startElement(String namespaceURI, String localName, String qName, Attributes atts) 
    throws SAXException { 

    if(depth!= 0) { 
     // we are in the middle of forwarding events. 
     // continue to do so. 
     depth++; 
     super.startElement(namespaceURI, localName, qName, atts); 
     return; 
    } 

    if(namespaceURI.equals("") && localName.equals("purchaseOrder")) { 
     // start a new unmarshaller 
     Unmarshaller unmarshaller; 
     try { 
      unmarshaller = context.createUnmarshaller(); 
     } catch(JAXBException e) { 
      // there's no way to recover from this error. 
      // we will abort the processing. 
      throw new SAXException(e); 
     } 
     unmarshallerHandler = unmarshaller.getUnmarshallerHandler(); 

     // set it as the content handler so that it will receive 
     // SAX events from now on. 
     setContentHandler(unmarshallerHandler); 

     // fire SAX events to emulate the start of a new document. 
     unmarshallerHandler.startDocument(); 
     unmarshallerHandler.setDocumentLocator(locator); 

     Enumeration e = namespaces.getPrefixes(); 
     while(e.hasMoreElements()) { 
      String prefix = (String)e.nextElement(); 
      String uri = namespaces.getURI(prefix); 

      unmarshallerHandler.startPrefixMapping(prefix,uri); 
     } 
     String defaultURI = namespaces.getURI(""); 
     if(defaultURI!=null) 
      unmarshallerHandler.startPrefixMapping("",defaultURI); 

     super.startElement(namespaceURI, localName, qName, atts); 

     // count the depth of elements and we will know when to stop. 
     depth=1; 
    } 
} 

public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 

    // forward this event 
    super.endElement(namespaceURI, localName, qName); 

    if(depth!=0) { 
     depth--; 
     if(depth==0) { 
      // just finished sending one chunk. 

      // emulate the end of a document. 
      Enumeration e = namespaces.getPrefixes(); 
      while(e.hasMoreElements()) { 
       String prefix = (String)e.nextElement(); 
       unmarshallerHandler.endPrefixMapping(prefix); 
      } 
      String defaultURI = namespaces.getURI(""); 
      if(defaultURI!=null) 
       unmarshallerHandler.endPrefixMapping(""); 
      unmarshallerHandler.endDocument(); 

      // stop forwarding events by setting a dummy handler. 
      // XMLFilter doesn't accept null, so we have to give it something, 
      // hence a DefaultHandler, which does nothing. 
      setContentHandler(new DefaultHandler()); 

      // then retrieve the fully unmarshalled object 
      try { 
       JAXBElement<PurchaseOrderType> result = 
     (JAXBElement<PurchaseOrderType>)unmarshallerHandler.getResult(); 

       // process this new purchase order 
       process(result.getValue()); 
      } catch(JAXBException je) { 
       // error was found during the unmarshalling. 
       // you can either abort the processing by throwing a SAXException, 
       // or you can continue processing by returning from this method. 
       System.err.println("unable to process an order at line "+ 
        locator.getLineNumber()); 
       return; 
      } 

      unmarshallerHandler = null; 
     } 
    } 
} 

public void process(PurchaseOrderType order) { 
    System.out.println("this order will be shipped to " 
     + order.getShipTo().getName()); 
} 

/** 
* Remembers the depth of the elements as we forward 
* SAX events to a JAXB unmarshaller. 
*/ 
private int depth; 

/** 
* Reference to the unmarshaller which is unmarshalling 
* an object. 
*/ 
private UnmarshallerHandler unmarshallerHandler; 


/** 
* Keeps a reference to the locator object so that we can later 
* pass it to a JAXB unmarshaller. 
*/ 
private Locator locator; 
public void setDocumentLocator(Locator locator) { 
    super.setDocumentLocator(locator); 
    this.locator = locator; 
} 


/** 
* Used to keep track of in-scope namespace bindings. 
* 
* For JAXB unmarshaller to correctly unmarshal documents, it needs 
* to know all the effective namespace declarations. 
*/ 
private NamespaceSupport namespaces = new NamespaceSupport(); 

public void startPrefixMapping(String prefix, String uri) throws SAXException { 
    namespaces.pushContext(); 
    namespaces.declarePrefix(prefix,uri); 

    super.startPrefixMapping(prefix, uri); 
} 

public void endPrefixMapping(String prefix) throws SAXException { 
    namespaces.popContext(); 

    super.endPrefixMapping(prefix); 
} 

}

Primer.xsd

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 

    <xsd:annotation> 
    <xsd:documentation xml:lang="en"> 
     Purchase order schema for Example.com. 
     Copyright 2000 Example.com. All rights reserved. 
    </xsd:documentation> 
    </xsd:annotation> 


    <xsd:element name="purchaseOrders"> 
    <xsd:complexType> 
     <xsd:sequence> 
     <xsd:element ref="purchaseOrder" minOccurs="0" maxOccurs="unbounded"/> 
     </xsd:sequence> 
    </xsd:complexType> 
    </xsd:element> 


    <xsd:element name="purchaseOrder" type="PurchaseOrderType"/> 

    <xsd:element name="comment" type="xsd:string"/> 

    <xsd:complexType name="PurchaseOrderType"> 
    <xsd:sequence> 
     <xsd:element name="shipTo" type="USAddress"/> 
     <xsd:element name="billTo" type="USAddress"/> 
     <xsd:element ref="comment" minOccurs="0"/> 
     <xsd:element name="items" type="Items"/> 
    </xsd:sequence> 
    <xsd:attribute name="orderDate" type="xsd:date"/> 
    </xsd:complexType> 

    <xsd:complexType name="USAddress"> 
    <xsd:sequence> 
     <xsd:element name="name" type="xsd:string"/> 
     <xsd:element name="street" type="xsd:string"/> 
     <xsd:element name="city" type="xsd:string"/> 
     <xsd:element name="state" type="xsd:string"/> 
     <xsd:element name="zip" type="xsd:decimal"/> 
    </xsd:sequence> 
    <xsd:attribute name="country" type="xsd:NMTOKEN" 
        fixed="US"/> 
    </xsd:complexType> 

    <xsd:complexType name="Items"> 
    <xsd:sequence> 
     <xsd:element name="item" minOccurs="0" maxOccurs="unbounded"> 
     <xsd:complexType> 
      <xsd:sequence> 
      <xsd:element name="productName" type="xsd:string"/> 
      <xsd:element name="quantity"> 
       <xsd:simpleType> 
       <xsd:restriction base="xsd:positiveInteger"> 
        <xsd:maxExclusive value="100"/> 
       </xsd:restriction> 
       </xsd:simpleType> 
      </xsd:element> 
      <xsd:element name="USPrice" type="xsd:decimal"/> 
      <xsd:element ref="comment" minOccurs="0"/> 
      <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/> 
      </xsd:sequence> 
      <xsd:attribute name="partNum" type="SKU" use="required"/> 
     </xsd:complexType> 
     </xsd:element> 
    </xsd:sequence> 
    </xsd:complexType> 

    <!-- Stock Keeping Unit, a code for identifying products --> 
    <xsd:simpleType name="SKU"> 
    <xsd:restriction base="xsd:string"> 
     <xsd:pattern value="\d{3}-[A-Z]{2}"/> 
    </xsd:restriction> 
    </xsd:simpleType> 

</xsd:schema> 

test.xml

<purchaseOrders> 
     <!-- 1st --> 
     <purchaseOrder orderDate="1999-10-20"> 
     <shipTo country="US"> 
      <name>Alice Smith</name> 
      <street>123 Maple Street</street> 
      <city>Cambridge</city> 
      <state>MA</state> 
      <zip>12345</zip> 
     </shipTo> 
     <billTo country="US"> 
      <name>Robert Smith</name> 
      <street>8 Oak Avenue</street> 
      <city>Cambridge</city> 
      <state>MA</state> 
      <zip>12345</zip> 
     </billTo> 
     <items/> 
     </purchaseOrder> 
    </purchaseOrders> 
+0

Cuando ejecuto su ejemplo me sale "este pedido será enviado a Alice Smith". –

+0

Sí, ¡correcto! El código funciona, pero si lo cambias de esa manera, debería leer "shipTo" en lugar de "purchaseOrder", el error está apareciendo – jeven

Respuesta

3

Su muestra es excesivamente complicada (> 300 líneas). Por favor, ¿puedes intentar que encaje en 30 líneas de código?

En la práctica, JAXB puede deserializar una corriente con 2 líneas de código (suponiendo que sus clases son anotado correctamente):

private <T> T parse(URL url, Class<T> clazz) throws JAXBException { 
    Unmarshaller unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller(); 
    return clazz.cast(unmarshaller.unmarshal(url)); 
} 

Ver this complete sample (con pruebas) por más.

Y this article para obtener más información sobre el tema.

+0

+1 por sugerir que se publique una anotación del problema. –

0

Se podría aprovechar una SAXSource para obtener el comportamiento que busca:

InputSource inputSource = new InputSource(new File(args[i]).toURL().toExternalForm()); 
SAXSource saxSource = new SAXSource(reader, inputSource); 
unmarshaller.unmarshal(saxSource, TargetClass.class); 

Ejemplo completo:

import java.io.File; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.parsers.SAXParserFactory; 
import javax.xml.transform.sax.SAXSource; 

import org.xml.sax.InputSource; 
import org.xml.sax.XMLReader; 

public class Main { 
    public static void main(String[] args) throws Exception { 

     // create JAXBContext for the primer.xsd 
     JAXBContext context = JAXBContext.newInstance("primer"); 

     // create a new XML parser 
     SAXParserFactory factory = SAXParserFactory.newInstance(); 
     factory.setNamespaceAware(true); 
     XMLReader reader = factory.newSAXParser().getXMLReader(); 

     // prepare a Splitter 
     Splitter splitter = new Splitter(context); 

     // connect two components 
     reader.setContentHandler(splitter); 

     Unmarshaller unmarshaller = context.createUnmarshaller(); 

     for(int i=0; i<args.length; i++) { 
      // parse all the documents specified via the command line. 
      // note that XMLReader expects an URL, not a file name. 
      // so we need conversion. 
      InputSource inputSource = new InputSource(new File(args[i]).toURL().toExternalForm()); 
      SAXSource saxSource = new SAXSource(reader, inputSource); 
      unmarshaller.unmarshal(saxSource, TargetClass.class); 
     } 
    } 

} 
3

tuve este problema precisa; tratando de usar el ejemplo de desmaterialización parcial de la implementación de referencia jaxb.

La solución que he decidido es agregar un com.sun.xml.bind.api.ClassResolver personalizado en el unmarshaller creado en el método startElement, arriba.Ver:

try { 
     unmarshaller = context.createUnmarshaller(); 
     unmarshaller.setProperty(ClassResolver.class.getName(), myClassResolver); 
} catch(JAXBException e) { 
... 

Aquí es un dispositivo de resolución maqueta ...

new ClassResolver() 
    { 
     @Override 
     public Class<?> resolveElementName(String nsUri, String localName) throws Exception 
     { 
      if(MY_NAMESPACE.equals(nsUri) && MY_BAR.equals(localName)) 
       return BarType.class; 
      else 
       return null; 
     } 
    } 
0

Por lo general, este error se produce cuando usted está tratando de pasar a la clase elemento hijo como argumento para jaxb ejemplo. intente pasar la clase raíz como argumento y compruebe si funcionará o no

0

Esta es la función que uso para tales casos.

/** 
* Loads an xml with a non <code>XmlRootElement</code> annotated element 
* 
* @param <T> Class to be unserialized 
* @param xmlStream {@link InputStream} The input stream of the xml file 
* @param modelClass <code>.class</code> of the model to read. This class may or not have the <code>XmlRootElement</code> annotation 
* @return {@link AppModel} Instance of the model 
* @throws JAXBException Error while reading the xml from the input stream 
*/ 
public static <T> T readNonRootDataModelFromXml(InputStream xmlStream, Class<T> modelClass) throws JAXBException 
{ 
    JAXBContext jaxbContext = JAXBContext.newInstance(modelClass); 
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
    JAXBElement<T> xmlRootElement = unmarshaller.unmarshal(new StreamSource(xmlStream), modelClass); 
    return (T) xmlRootElement.getValue(); 
} 

Puedes ponerlo en tu clase estática Utils.

Cuestiones relacionadas