5

大規模で複雑な xml を解析してフラット ファイルに書き込む必要があります。アドバイスをいただけますか?

ファイルサイズ: 500MB レコード数: 100K 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

6 に答える 6

1

Spring Batch 内で、前述よりも少し具体的に動作する独自の Stax イベント アイテム リーダーの実装を作成しました。基本的に、要素をマップに詰め込み、ItemProcessor に渡します。そこから、「GatheredElement」から単一のオブジェクト (CompositeItemProcessor を参照) に自由に変換できます。StaxEventItemReader からのコピーと貼り付けが少しあることをお詫びしますが、避けられないと思います。

ここからは、好きな OXM マーシャラーを自由に使用できます。たまたま 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; 
    }

収集された要素クラスは非常に基本的です。

public class GatheredElementItem<T> {
    private final T item;
    private final Map<String, String> gatheredElements;
    ...
}
于 2013-01-04T20:32:15.823 に答える
0

次のようなETLツールを試してみてください

Pentahoデータ統合(別名ケトル)

于 2012-12-30T13:45:48.750 に答える
0

ファイルサイズを扱っていませんが、あなたの問題を考慮して、XMLプル解析とフラットファイルに書き込むスマートコードの組み合わせを推測しています( Java ヒープを使い果たしたくないので、これが役立つかもしれません)。XML プル解析の使用に関するチュートリアルとサンプル コードを Google で簡単に検索できます。

于 2012-12-19T12:23:38.453 に答える
0

JAXB/Spring Batch 以外のソリューションを受け入れる場合は、SAX パーサーを確認することをお勧めします。

これは、XML ファイルを解析するよりイベント指向の方法であり、解析中にターゲット ファイルに直接書き込みたい場合に適した方法です。SAX パーサーは、xml コンテンツ全体をメモリに読み込むのではなく、入力ストリーム内の要素に遭遇したときにメソッドをトリガーします。私が経験した限り、これは非常にメモリ効率の高い処理方法です。

Stax-Solution と比較して、SAX はデータをアプリケーションに「プッシュ」します。これは、状態を維持する必要があることを意味します (現在のタグのように)。そのため、現在の場所を追跡する必要があります。それが本当に必要なものかどうかはわかりません

次の例では、構造内の xml ファイルを読み取り、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 {}
}
于 2013-01-30T13:15:32.393 に答える
0

最後に、カスタマイズした StaxEventItemReader を実装します。

  1. 構成fragmentRootElementName

  2. 自分の 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. 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();
    

    }

于 2012-12-30T13:34:41.760 に答える
0

Declarative Stream Mapping (DSM)ストリーム解析ライブラリを使用できます。JSON と XML の両方を処理できます。XML ファイルをメモリにロードしません。DSM は、YAML または JSON 構成で定義したデータのみを処理します。

XML を読みながらメソッドを呼び出すことができます。これにより、XML を部分的に処理することができます。この部分的に読み取られた XML データを Java オブジェクトにデシリアライズできます。

それを使用して複数のスレッドで読み取ることもできます。

この回答で良い例を見つけることができます

STAX Parser を使用して XML を異なるオブジェクトの 3 つのリストにアンマーシャリングする

JAVA - 巨大な (特大の) JSON ファイルを解析するための最良の方法(XML についても同じ)

于 2020-04-09T11:01:14.670 に答える