2012-12-19 15 views
5

Potrzebuję parsować duży złożony xml i pisać do płaskiego pliku, czy możesz dać radę? Wielkośćjak parsować duży złożony xml

pliku: 500MB liczyć rekord: 100K struktura XML:

<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

istnieje specyficzny język” zamierzacie użyć? –

+0

Czy struktura pliku musi być sprawdzona, czy może zakładać, że jest poprawna na sé? – Thilo

+0

Używam Java, JAXB/Spring Batch jest preferowaną opcją, czytałem wiele postów, ale wciąż nie mam pojęcia, jak skutecznie przetwarzać powyższy xml. – Weber

Odpowiedz

0

I nie mieć do czynienia z takimi ogromnymi rozmiarów plików, ale biorąc pod uwagę swój problem, ponieważ chcesz analizować i pisać do płaskiego pliku, zgaduję, że kombinacja XML Pull Parsing i inteligentny kod do zapisu do płaskiego pliku (this might help), ponieważ nie chcemy wyczerpać sterty Java. Możesz wykonać szybkie wyszukiwanie w Google samouczków i przykładowego kodu przy użyciu funkcji XML Pull Parsing.

+0

Tak, JAXB/Spring Batch jest preferowaną opcją, ale nie ma pojęcia, jak skutecznie analizować powyższy złożony xml. Jestem początkującym w dużym parsowaniu xml. Wszelkie komentarze zostaną docenione. – Weber

0

Na koniec zaimplementowałem dostosowany StaxEventItemReader.

  1. Config fragmentRootElementName

  2. Config własne 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. Dodaj następujący fragment w 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

Within wiosennym wsadowym pisałem moją implementację czytnika własny artykuł wydarzenie Stax, który działa trochę dokładniej niż poprzednio wspomniane. Zasadniczo wrzucam elementy do mapy, a następnie przekazuję je do ItemProcessor. Stamtąd możesz przekształcić go w pojedynczy obiekt (zobacz CompositeItemProcessor) z "GatheredElement". Przepraszamy za posiadanie małej ilości kopii/wklejeń z StaxEventItemReader, ale nie sądzę, że można tego uniknąć.

Stamtąd możesz korzystać z dowolnego elementu Marshallera OXM, ja też używam 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; 
    } 

Zgromadzone klasa elementem jest dość prosty:

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

Jeśli przyjąć rozwiązanie bok JAXB/wiosna wsadowego, może warto spojrzeć na SAX Parser.

Jest to bardziej zorientowany na zdarzenia sposób analizowania plików XML i może być dobrym rozwiązaniem, jeśli chcesz bezpośrednio zapisywać do pliku docelowego podczas analizowania. Analizator składni SAX nie odczytuje całej zawartości xml w pamięci, ale uruchamia metody, gdy zawiera elementy w strumieniu wejściowym. O ile mi się to udało, jest to bardzo wydajny sposób przetwarzania pamięci.

W porównaniu do twojego Stax-Solution SAX "przesyła" dane do twojej aplikacji - oznacza to, że musisz utrzymywać stan (jak na przykład, w którym tagu jesteś skandalizowany), więc musisz śledzić bieżące Lokalizacja.Nie jestem pewien, czy to jest coś, co naprawdę wymaga

Poniższy przykład odczytuje w pliku XML w swojej strukturze i wypisuje cały tekst w GroupBMsg-tags:

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 {} 
}