Java、C#、Ruby、PHP、C / C ++など、使い慣れた言語を使用したいと思いますが、どの言語や擬似コードの例でも大歓迎です。
大きなXMLドキュメントをまだ有効なXMLである小さなセクションに分割する最良の方法は何ですか?私の目的では、それらをおよそ3分の1または4分の1に分割する必要がありますが、例を示すために、それらをn個のコンポーネントに分割するとよいでしょう。
Java、C#、Ruby、PHP、C / C ++など、使い慣れた言語を使用したいと思いますが、どの言語や擬似コードの例でも大歓迎です。
大きなXMLドキュメントをまだ有効なXMLである小さなセクションに分割する最良の方法は何ですか?私の目的では、それらをおよそ3分の1または4分の1に分割する必要がありますが、例を示すために、それらをn個のコンポーネントに分割するとよいでしょう。
DOM を使用した XML ドキュメントの解析はスケーリングしません。
このGroovyスクリプトは、StAX (Streaming API for XML) を使用して XML ドキュメントをトップレベルの要素 (ルート ドキュメントの最初の子と同じ QName を共有する) に分割します。非常に高速で、任意の大きなドキュメントを処理でき、大きなバッチ ファイルを小さな断片に分割する場合に非常に便利です。
Java 6 または StAX API 上の Groovy と、CLASSPATH のWoodstoxなどの実装が必要です。
import javax.xml.stream.*
pieces = 5
input = "input.xml"
output = "output_%04d.xml"
eventFactory = XMLEventFactory.newInstance()
fileNumber = elementCount = 0
def createEventReader() {
reader = XMLInputFactory.newInstance().createXMLEventReader(new FileInputStream(input))
start = reader.next()
root = reader.nextTag()
firstChild = reader.nextTag()
return reader
}
def createNextEventWriter () {
println "Writing to '${filename = String.format(output, ++fileNumber)}'"
writer = XMLOutputFactory.newInstance().createXMLEventWriter(new FileOutputStream(filename), start.characterEncodingScheme)
writer.add(start)
writer.add(root)
return writer
}
elements = createEventReader().findAll { it.startElement && it.name == firstChild.name }.size()
println "Splitting ${elements} <${firstChild.name.localPart}> elements into ${pieces} pieces"
chunkSize = elements / pieces
writer = createNextEventWriter()
writer.add(firstChild)
createEventReader().each {
if (it.startElement && it.name == firstChild.name) {
if (++elementCount > chunkSize) {
writer.add(eventFactory.createEndDocument())
writer.flush()
writer = createNextEventWriter()
elementCount = 0
}
}
writer.add(it)
}
writer.flush()
もちろん、いつでもトップレベルの要素を抽出できます(これが必要な粒度であるかどうかはあなた次第です)。C#では、XmlDocumentクラスを使用します。たとえば、XMLファイルが次のようになっている場合:
<Document>
<Piece>
Some text
</Piece>
<Piece>
Some other text
</Piece>
</Document>
次に、次のようなコードを使用して、すべてのピースを抽出します。
XmlDocument doc = new XmlDocument();
doc.Load("<path to xml file>");
XmlNodeList nl = doc.GetElementsByTagName("Piece");
foreach (XmlNode n in nl)
{
// Do something with each Piece node
}
ノードを取得したら、コード内でノードを使用して何かを実行するか、ノードのテキスト全体を独自のXMLドキュメントに転送して、独立したXMLの一部であるかのように操作することができます(保存を含む)。ディスクに戻すなど)。
DannySmurfがここで触れているように、それはすべてxmlドキュメントの構造に関するものです。
巨大な「トップレベル」タグが2つしかない場合、それをマージして元に戻し、有効なxmlとして1つずつ読み取ることができるように分割することは非常に困難です。
DannySmurfsの例のように、個別の部分がたくさんあるドキュメントを考えると、かなり簡単なはずです。
疑似C#のいくつかの大まかなコード:
int nrOfPieces = 5;
XmlDocument xmlOriginal = some input parameter..
// construct the list we need, and fill it with XmlDocuments..
var xmlList = new List<XmlDocument>();
for (int i = 0; i < nrOfPieces ; i++)
{
var xmlDoc = new XmlDocument();
xmlDoc.ChildNodes.Add(new XmlNode(xmlOriginal.FistNode.Name));
xmlList.Add(xmlDoc);
}
var nodeList = xmlOriginal.GetElementsByTagName("Piece")M
// Copy the nodes from the original into the pieces..
for (int i = 0; i < nodeList .Count; i++)
{
var xmlDoc = xmlList[i % nrOfPieces];
var nodeToCopy = nodeList[i].Clone();
xmlDoc.FirstNode.ChildNodes.Add(nodeToCopy);
}
これにより、正しいxmlを含むn個のドキュメントが得られ、それらをマージして戻すことができます。
しかし、繰り返しになりますが、それはxmlファイルに依存します。
これは答えというよりはコメントですが、そうではありません。
XmlDocument doc = new XmlDocument();
doc.Load("path");
ファイル全体を一度に読みますか?トーマスの質問の見た目から、彼は大きなファイルを読むことを心配していて、プロセスを分解したいと思っているので、私がポイントを上げるべきだと思っただけです。
ファイル全体を一度に読み取ります。ただし、私の経験では、ファイルを読み取り、処理(つまり、分割)を行ってから作業を続行する場合、XmlDocumentは作成/読み取り/収集のサイクルを非常に迅速に実行するため、それはおそらく問題ではないでしょう。
もちろん、それは「大きな」ファイルが何であるかに依存します。30 MBのXMLファイル(XMLファイルの場合は大きいと思います)の場合、おそらく違いはありません。500 MBのXMLファイルの場合、大量のRAMがないシステムでは、XmlDocumentの使用が非常に問題になります(ただし、その場合、XmlReaderを使用してファイルを手動で選択する時間がより重要になると思います。障害)。
入力ファイルと出力ファイルのサイズに関係なく、少量のメモリのみを使用して foxe (Firstobject の無料の XML エディター) を使用してXMLファイルを分割する方法を示す YouTube ビデオを作成しました。
この CMarkup XML リーダー (プル パーサー) および XML ライター ソリューションのメモリ使用量は、入力ファイルから出力ファイルに個別に転送されるサブドキュメントのサイズ、または 16 KB の最小ブロック サイズによって異なります。
スプリット() { CMarkup xmlInput、xmlOutput; xmlInput.Open( "50MB.xml", MDF_READFILE ); int nObjectCount = 0、nFileCount = 0; while ( xmlInput.FindElem("//ACT") ) { 場合 ( nObjectCount == 0 ) { ++nFileCount; xmlOutput.Open( "ピース" + nFileCount + ".xml", MDF_WRITEFILE ); xmlOutput.AddElem( "ルート" ); xmlOutput.IntoElem(); } xmlOutput.AddSubDoc( xmlInput.GetSubDoc() ); ++nObjectCount; 場合 ( nObjectCount == 5 ) { xmlOutput.Close(); nObjectCount = 0; } } if ( nObjectCount ) xmlOutput.Close(); xmlInput.Close(); nFileCount を返します。 }
C# と .NET 3.5 を使用しているようです。XmlReader を使用してファイル ストリームで yield 型のアルゴリズムを使用することを提案するいくつかの投稿に出くわしました。
パスを開始するためのいくつかのブログ投稿を次に示します。
どのタイプの処理を行っているかはわかりませんが、非常に大きな XML の場合、私は常にイベント ベースの処理のファンでした。Java のバックグラウンドがあるからかもしれませんが、私は SAX が本当に好きです。独自の状態管理を行う必要がありますが、それを乗り越えれば、XML を解析する非常に効率的な方法になります。
私はこれについてユーフォリックと一緒に行きます。非常に大きなファイルの場合、SAX (またはその他のストリーミング パーサー) が処理に非常に役立ちます。DOM を使用すると、トップ レベルのノードだけを収集できますが、そのためにはドキュメント全体を解析する必要があります。ストリーミング パーサーとイベントベースの処理を使用すると、関心のないノードを「スキップ」できます。処理を高速化します。
Perl に完全にアレルギーがあるわけではない場合、XML::Twigにはxml_splitという名前のツールが付属しており 、ドキュメントを分割して整形式の XML セクションを作成できます。ツリーのレベル、サイズ、または XPath 式で分割できます。