2011-06-26 26 views
9

Tôi đang sử dụng một phần -unmarshalling ví dụ về JAXB, nhưng tôi không thể unmarshal XML Elements mà không phải là trên gốc cấp (nguyên nhân họ không có một thẻ @XmlRootElement). Trong ví dụ của tôi, tôi đã cố gắng đọc shipTo-Element thay vì purchaseOrder-Element.JAXB Elements phần-unmarshalling mà không @XMLRootElement

Thông thường tôi sẽ làm việc với JAXBElement unmarshal (Nguồn nguồn, Class declarationType) nhưng vì ví dụ đang sử dụng UnmarshallerHandler và XMLFilterImpl, tôi không biết phải nói cho Jaxb biết Class nên sử dụng ở đâu.

Thông báo lỗi của tôi là: Gây ra bởi: javax.xml.bind.UnmarshalException: phần tử không mong muốn (uri: "", local: "shipTo"). Các phần tử mong đợi là < {} nhận xét>, < {} purchaseOrder>, < {} purchaseOrders>

Tôi googled xung quanh rất nhiều, nhưng không tìm thấy gì hữu ích.

Đây là đoạn mã ví dụ từ JAXB-Trang 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

Khi tôi chạy ví dụ của bạn, tôi nhận được "đơn đặt hàng này sẽ được chuyển tới Alice Smith". –

+0

Có, đúng! Mã hoạt động, nhưng nếu bạn thay đổi nó theo cách mà nó nên đọc "shipTo" thay vì "purchaseOrder", lỗi sẽ xuất hiện – jeven

Trả lời

3

mẫu của bạn được overcomplicated (> 300 dòng). Xin vui lòng, bạn có thể cố gắng làm cho nó phù hợp trên 30 dòng mã?

Trên thực tế, JAXB thể unmarshal một dòng với 2 dòng mã (giả định rằng lớp học của bạn được chú thích một cách chính xác):

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

Xem this complete sample (với các bài kiểm tra) để biết thêm.

this article để biết thêm về chủ đề này.

+0

+1 để đề xuất đăng sự cố thu hẹp của sự cố –

0

Bạn có thể tận dụng một SAXSource để có được hành vi mà bạn đang tìm kiếm:

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

Full Ví dụ:

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

Tôi đã có vấn đề chính xác này; cố gắng sử dụng ví dụ một phần unmarshalling từ việc thực hiện tham chiếu jaxb.

Giải pháp mà tôi đã giải quyết là thêm một com.sun.xml.bind.api.ClassResolver tùy chỉnh vào trình gỡ không được tạo trong phương thức startElement, ở trên.Xem:

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

Dưới đây là một trình giải quyết mockup ...

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

Thông thường lỗi này được ném khi bạn đang cố gắng để vượt qua lớp phần tử con như là đối số để JAXB ví dụ. thử vượt qua lớp gốc làm đối số và kiểm tra xem nó có hoạt động hay không

0

Đây là chức năng tôi sử dụng cho các trường hợp như vậy.

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

Bạn có thể đặt nó trong lớp tĩnh Utils.

Các vấn đề liên quan