2011-06-26 64 views
9

我使用JAXB局部解組的例子,但我無法解組XML的元素不屬於根級(因爲他們沒有一個@XmlRootElement標籤)。在我的例子中,我嘗試閱讀shipTo-Element而不是purchaseOrder-Element。JAXB局部解組元素,而@XmlRootElement

通常我會使用JAXBElement unmarshal(Source source,Class declaredType),但由於該示例使用的是UnmarshallerHandler和XMLFilterImpl,因此我不知道應該在哪裏告訴Jaxb應該使用哪個類。

我的錯誤消息:由:javax.xml.bind.UnmarshalException:意想不到元件(URI: 「」,本地: 「SHIPTO」)。預期的元素是< {} comment>,< {} purchaseOrder>,< {} purchaseOrders>

我搜索了很多,但沒有找到任何有用的東西。

這裏是從JAXB-頁面的示例代碼:

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

當我運行你的例子時,我得到「這個訂單將被運到Alice Smith」。 –

+0

是的,正確!代碼的作品,但如果你改變它應該閱讀「shipTo」而不是「purchaseOrder」錯誤來了 – jeven

回答

3

你的樣品被過於複雜的(> 300線)。請你能試着讓它適合30行代碼嗎?

在實踐中,可以JAXB解組流與2行代碼(假設你的類被正確標註的):

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

參見this complete sample(帶測試)爲多。

而且this article甚至更​​多的關於這個問題的。

+0

+1建議發佈一個縮小的問題 –

0

你可以充分利用SAXSource得到你正在尋找的行爲:

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

完整的例子:我有這樣精確的問題

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

;試圖使用jaxb參考實現中的部分解組示例。

的解決方案,我在解決是一個自定義的com.sun.xml.bind.api.ClassResolver加入到在的startElement方法創建的解組,上面。請參閱:

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

這裏有一個實體模型解析...

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

通常這個錯誤是,當你試圖通過子元素類作爲參數傳遞給JAXB實例拋出。嘗試傳遞根類作爲參數,並檢查它是否會起作用

0

這是我用於這種情況下的功能。

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

您可以將它放在您的靜態Utils類中。