2017-08-17 38 views
0

我要解析XML這根元素是一個列表:JAXB - 解析XML列表順序(一個接一個)

<SomeObjectsCollection> 
    <SomeObject><!-- content1 --></SomeObject> 
    <SomeObject><!-- content2 --></SomeObject> 
    <SomeObject><!-- content3 --></SomeObject> 
    <!-- and hundreds more --> 
</SomeObjectsCollection> 

我不想整個XML解析到內存性能的原因。我寧願寫一些像Iterable<SomeObjetType>這樣的東西,它不會強制用戶在內存中保留整個非編組列表,只是逐個處理它。

到目前爲止,我寫了這樣的類,它實現Iterable<SomeObject>和我自己的Iterator內:

public class SomeObjectsIterableParser implements Iterable<SomeObjectType> { 

    private final Unmarshaller jaxbUnmarshaller; 
    private final XMLStreamReader xmlReader; 

    public SomeObjectsIterableParser(Schema schema, java.io.Reader xmlStringReader) throws ExtractorException { 
    try { 
     jaxbUnmarshaller = JAXBContext.newInstance(SomeObjectType.class).createUnmarshaller(); 
     xmlReader = XMLInputFactory.newFactory().createXMLStreamReader(xmlStringReader); 
    } catch (JAXBException | XMLStreamException e) { 
     throw new ExtractorException("Could not create jaxbUnmarshaller", e); 
    } 
    jaxbUnmarshaller.setSchema(schema); //turns on schema validation 

    //Move reader to first occurence of SomeObject - really necessary? 
    try { 
     while (xmlReader.hasNext()) { 
     if (!xmlReader.isStartElement() || !xmlReader.getLocalName().equals("SomeObject")) 
      xmlReader.next(); 
     else break; 
     } 
    } catch (XMLStreamException e) { 
     e.printStackTrace(); 
    } 

    } 

    @Override 
    public Iterator<SomeObjectType> iterator() { 
    return new MyIterator(); 
    } 

    class MyIterator implements Iterator<SomeObjectType> { 

    @Override 
    public boolean hasNext() { 
     try { 
     return xmlReader.hasNext(); 
     } catch (XMLStreamException e) { 
     throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public SomeObjectType next() { 
     try { 
     return (SomeObjectType) jaxbUnmarshaller.unmarshal(xmlReader); 
     } catch (JAXBException | XMLStreamException e) { 
     throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public void remove() { 
     throw new UnsupportedOperationException("Not supported yet"); 
    } 
    } 
} 

我收到例外next()法消息:org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 17; cvc-elt.1: Cannot find the declaration of element 'SomeObject'.

我在做什麼錯?

+1

我們很需要的一切對於一個完整的診斷,但最有可能的架構聲明只是作爲一個子而不是在這個模式中可接受的獨立元素。由於您的Unmarshaller到目前爲止看不到您的XMLReader讀取的內容,因此無法知道實際上是的子項。你需要以不同的方式聲明你的模式來獨立聲明這樣的元素,或者以另一種方式驗證(我知道沒有) – kumesana

+0

@kumesana你是對的,這是問題(但不是唯一的問題)。我創建了連接問題:https://stackoverflow.com/questions/45733478/jaxb-schema-validation-when-unmarshalling-non-root-element – jaskmar

回答

0

我發現the article,它描述了我想要做的事情。因此,我寫了這樣的代碼:

public class SomeObjectsIterableParser implements Iterable<SomeObjectType> { 

    private final Unmarshaller jaxbUnmarshaller; 
    private final XMLStreamReader xmlReader; 

    public SomeObjectsIterableParser(Schema schema, Reader SomeObjectResponse) throws ExtractorException { 
    try { 
     jaxbUnmarshaller = JAXBContext.newInstance(SomeObjectType.class).createUnmarshaller(); 
     xmlReader = XMLInputFactory.newFactory().createXMLStreamReader(SomeObjectResponse); 
    } catch (JAXBException | XMLStreamException e) { 
     throw new ExtractorException("Could not create jaxbUnmarshaller", e); 
    } 
    //jaxbUnmarshaller.setSchema(schema); //schema can handle only root element 
    advanceReaderToFirstProfile(); 
    } 

    private void advanceReaderToFirstProfile() { 
    try { 
     xmlReader.nextTag(); 
     while(!xmlReader.getLocalName().equals("SomeObject")) { 
     xmlReader.nextTag(); 
     } 
    } catch (XMLStreamException e) { 
     e.printStackTrace(); 
    } 
    } 

    @Override 
    public Iterator<SomeObjectType> iterator() { 
    return new MyIterator(); 
    } 

    class MyIterator implements Iterator<SomeObjectType> { 

    @Override 
    public boolean hasNext() { 
     try { 
     if (xmlReader.isWhiteSpace() && xmlReader.hasNext()) { 
      //ommit witespaces 
      xmlReader.nextTag(); 
     } 
     } catch (XMLStreamException e) { 
     throw new RuntimeException(e); 
     } 
     return xmlReader.isStartElement() 
      && xmlReader.getLocalName().equals("SomeObject"); 
    } 

    @Override 
    public SomeObjectType next() { 
     try { 
     JAXBElement<SomeObjectType> element = jaxbUnmarshaller.unmarshal(xmlReader, SomeObjectType.class); 
     return element.getValue(); 
     } catch (JAXBException | XMLStreamException e) { 
     throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public void remove() { 
     throw new UnsupportedOperationException("Not supported yet"); 
    } 
    } 
} 

注意的3認爲:

  1. 架構驗證,不能適用於非根元素。有相關的問題here
  2. 使用語法:
    JAXBElement<SomeObjectType> element = jaxbUnmarshaller.unmarshal(xmlReader, SomeObjectType.class);
    而不是
    (SomeObjectType) jaxbUnmarshaller.unmarshal(xmlReader);
    否則,您會收到一條異常:

    了java.lang.RuntimeException:javax.xml.bind.UnmarshalException

    • 與鏈接的異常:
      [com.sun.istack.internal.SAXParseException2; lineNumber:2; columnNumber:22;意外元素(uri:「」,local:「SomeObject」)。預期元素是(無)]
  3. 比例子中更仔細地處理異常。 IterableIterator接口不允許您拋出非運行時異常。