2012-12-19 9 views
5

Ich muss ein großes komplexes XML analysieren und in eine Flat-Datei schreiben, können Sie etwas beraten?wie große komplexe XML zu analysieren

Dateigröße: 500 MB Rekordzählimpuls: 100K XML-Struktur:

<Msg> 

    <MsgHeader> 
     <!--Some of the fields in the MsgHeader need to be map to a java object--> 
    </MsgHeader> 

    <GroupA> 
     <GroupAHeader/> 
     <!--Some of the fields in the GroupAHeader need to be map to a java object--> 
     <GroupAMsg/> 
     <!--50K records--> 
     <GroupAMsg/> 
     <GroupAMsg/> 
     <GroupAMsg/> 
    </GroupA> 

    <GroupB> 
     <GroupBHeader/> 
     <GroupBMsg/> 
     <!--50K records--> 
     <GroupBMsg/> 
     <GroupBMsg/> 
     <GroupBMsg/> 
    </GroupB> 

</Msg> 
+4

gibt es eine bestimmte Sprache, die Sie‘ wirst du benutzen? –

+0

Muss die Struktur der Datei überprüft werden, oder können Sie davon ausgehen, dass sie per se gültig ist? – Thilo

+0

Ich benutze Java, JAXB/Spring Batch ist die bevorzugte Option, ich habe viele Beiträge gelesen, aber immer noch keine Ahnung, wie man über Xml effektiv verarbeitet. – Weber

Antwort

0

ich mit so großen Dateigrößen nicht behandelt haben, aber Ihr Problem bedenkt, da Sie die analysieren wollen und schreiben zu einer flachen Datei, schätze ich eine Kombination XML Pull Parsing und Smart-Code, um in die flache Datei (this might help) zu schreiben, weil wir den Java-Heap nicht erschöpfen wollen. Sie können eine schnelle Google-Suche nach Tutorials und Beispielcode für die Verwendung von XML Pull Parsing durchführen.

+0

Ja, JAXB/Spring Batch ist die bevorzugte Option, aber Sie haben keine Ahnung, wie Sie komplexes XML effizient parsen können. Ich bin ein Neuling in großen XML-Parsing. Irgendwelche Kommentare werden geschätzt. – Weber

0

Endlich implementiere ich einen angepassten StaxEventItemReader.

  1. Config fragmentRootElementName

  2. Config mein eigenes manualHandleElement

    <property name="manualHandleElement"> 
    <list> 
        <map> 
         <entry> 
          <key><value>startElementName</value></key> 
          <value>GroupA</value> 
         </entry> 
         <entry> 
          <key><value>endElementName</value></key> 
          <value>GroupAHeader</value> 
         </entry> 
         <entry> 
          <key><value>elementNameList</value></key> 
           <list> 
             <value>/GroupAHeader/Info1</value> 
             <value>/GroupAHeader/Info2</value> 
           </list> 
         </entry> 
        </map> 
    </list> 
    

  3. Add folgende Fragment in MyStaxEventItemReader.doRead()

    while(true){ 
    if(reader.peek() != null && reader.peek().isStartElement()){ 
        pathList.add("/"+((StartElement) reader.peek()).getName().getLocalPart()); 
        reader.nextEvent(); 
        continue; 
    } 
    if(reader.peek() != null && reader.peek().isEndElement()){ 
        pathList.remove("/"+((EndElement) reader.peek()).getName().getLocalPart()); 
        if(isManualHandleEndElement(((EndElement) reader.peek()).getName().getLocalPart())){ 
         pathList.clear(); 
         reader.nextEvent(); 
         break; 
        } 
        reader.nextEvent(); 
        continue; 
    } 
    if(reader.peek() != null && reader.peek().isCharacters()){ 
        CharacterEvent charEvent = (CharacterEvent)reader.nextEvent(); 
        String currentPath = getCurrentPath(pathList); 
        String startElementName = (String)currentManualHandleStartElement.get(MANUAL_HANDLE_START_ELEMENT_NAME); 
        for(Object s : (List)currentManualHandleStartElement.get(MANUAL_HANDLE_ELEMENT_NAME_LIST)){ 
         if(("/"+startElementName+s).equals(currentPath)){ 
          map.put(getCurrentPath(pathList), charEvent.getData()); 
          break; 
         } 
        } 
        continue; 
    } 
    
    reader.nextEvent(); 
    

    }

1

Innerhalb Spring Batch habe ich meine eigenen stax Ereignis Artikel Leser Implementierung geschrieben, die ein wenig spezifisch mehr arbeitet als zuvor erwähnt. Grundsätzlich stopfe ich Elemente einfach in eine Map und übergebe sie dann in den ItemProcessor. Von dort aus können Sie es aus dem "GatheredElement" in ein einzelnes Objekt (siehe CompositeItemProcessor) transformieren. Entschuldigen Sie, dass Sie etwas vom StaxEventItemReader kopieren/einfügen, aber ich glaube nicht, dass es vermeidbar ist.

Von hier aus können Sie den OXM Marshaller benutzen, den Sie möchten, ich benutze auch JAXB.

public class ElementGatheringStaxEventItemReader<T> extends StaxEventItemReader<T> { 
    private Map<String, String> gatheredElements; 
    private Set<String> elementsToGather; 
    ... 
    @Override 
    protected boolean moveCursorToNextFragment(XMLEventReader reader) throws NonTransientResourceException { 
     try { 
      while (true) { 
       while (reader.peek() != null && !reader.peek().isStartElement()) { 
        reader.nextEvent(); 
       } 
       if (reader.peek() == null) { 
        return false; 
       } 
       QName startElementName = ((StartElement) reader.peek()).getName(); 
       if(elementsToGather.contains(startElementName.getLocalPart())) { 
        reader.nextEvent(); // move past the actual start element 
        XMLEvent dataEvent = reader.nextEvent(); 
        gatheredElements.put(startElementName.getLocalPart(), dataEvent.asCharacters().getData()); 
        continue; 
       } 
       if (startElementName.getLocalPart().equals(fragmentRootElementName)) { 
        if (fragmentRootElementNameSpace == null || startElementName.getNamespaceURI().equals(fragmentRootElementNameSpace)) { 
         return true; 
        } 
       } 
       reader.nextEvent(); 

      } 
     } catch (XMLStreamException e) { 
      throw new NonTransientResourceException("Error while reading from event reader", e); 
     } 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    protected T doRead() throws Exception { 
     T item = super.doRead(); 
     if(null == item) 
      return null; 
     T result = (T) new GatheredElementItem<T>(item, new  HashedMap(gatheredElements)); 
     if(log.isDebugEnabled()) 
      log.debug("Read GatheredElementItem: " + result); 
     return result; 
    } 

Die Elementklasse versammelt ist ziemlich einfach:

public class GatheredElementItem<T> { 
    private final T item; 
    private final Map<String, String> gatheredElements; 
    ... 
} 
0

Wenn Sie eine Lösung zur Seite Batch JAXB/Frühjahr akzeptieren, Sie einen Blick auf die SAX-Parser haben möchten.

Dies ist eine eher ereignisorientierte Methode zum Parsen von XML-Dateien und kann ein guter Ansatz sein, wenn Sie während der Analyse direkt in die Zieldatei schreiben möchten. Der SAX-Parser liest nicht den gesamten XML-Inhalt in den Speicher, sondern löst Methoden aus, wenn Elemente im Eingabestream dekonzentriert werden. Soweit ich es erlebt habe, ist dies eine sehr speichereffiziente Art der Verarbeitung.

Im Vergleich zu Ihrer Stax-Lösung "schiebt" SAX die Daten in Ihre Anwendung - das heißt, Sie müssen den Zustand (wie in welchem ​​Tag sind Sie aktuell) pflegen, damit Sie Ihren aktuellen Stand halten müssen Lage.Ich bin mir nicht sicher, ob das etwas, das Sie wirklich

Das folgende Beispiel liest in einer XML-Datei in Ihrer Struktur erfordern und druckt sich der Text innerhalb GroupBMsg-Schlagwörter:

import java.io.FileReader; 
import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.InputSource; 
import org.xml.sax.Locator; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 
import org.xml.sax.helpers.XMLReaderFactory; 

public class SaxExample implements ContentHandler 
{ 
    private String currentValue; 

    public static void main(final String[] args) throws Exception 
    { 
     final XMLReader xmlReader = XMLReaderFactory.createXMLReader(); 

     final FileReader reader = new FileReader("datasource.xml"); 
     final InputSource inputSource = new InputSource(reader); 

     xmlReader.setContentHandler(new SaxExample()); 
     xmlReader.parse(inputSource); 
    } 

    @Override 
    public void characters(final char[] ch, final int start, final int length) throws  SAXException 
    { 
     currentValue = new String(ch, start, length); 
    } 

    @Override 
    public void startElement(final String uri, final String localName, final String  qName, final Attributes atts) throws SAXException 
    { 
     // react on the beginning of tag "GroupBMsg" <GroupBMSg> 
     if (localName.equals("GroupBMsg")) 
     { 
      currentValue=""; 
     } 
    } 

    @Override 
    public void endElement(final String uri, final String localName, final String  qName) throws SAXException 
    { 
     // react on the ending of tag "GroupBMsg" </GroupBMSg> 
     if (localName.equals("GroupBMsg")) 
     { 
      // TODO: write into file 
      System.out.println(currentValue); 
     } 
    } 


    // the rest is boilerplate code for sax 

    @Override 
    public void endDocument() throws SAXException {} 
    @Override 
    public void endPrefixMapping(final String prefix) throws SAXException {} 
    @Override 
    public void ignorableWhitespace(final char[] ch, final int start, final int length) 
     throws SAXException {} 
    @Override 
    public void processingInstruction(final String target, final String data) 
     throws SAXException {} 
    @Override 
    public void setDocumentLocator(final Locator locator) { } 
    @Override 
    public void skippedEntity(final String name) throws SAXException {} 
    @Override 
    public void startDocument() throws SAXException {} 
    @Override 
    public void startPrefixMapping(final String prefix, final String uri) 
     throws SAXException {} 
}