XMLを解析するためのテクニック


11

私は常にXMLの処理がやや面倒だと感じてきました。私はXMLパーサーの実装について話しているのではなく、ノードごとにXMLを処理するSAXパーサーのような既存のストリームベースのパーサーの使用について話しいるのです。

はい、これらのパーサーのさまざまなAPIを学ぶのは非常に簡単ですが、XMLを処理するコードを見ると、常に多少複雑になることがわかります。本質的な問題は、XMLドキュメントが個々のノードに論理的に分離されているにもかかわらず、データの種類と属性が実際のデータから分離されていることが多いことです。したがって、特定のノードを個別に処理する場合、現在の場所次に何をする必要があるかを判断するために、多くの余分な状態を維持する必要があります。

たとえば、典型的なXMLドキュメントからスニペットが与えられた場合:

<book>
  <title>Blah blah</title>
  <author>Blah blah</author>
  <price>15 USD</price>
</book>

...本のタイトルを含むテキストノードに出会ったとき、どのように判断しますか?イテレータのように動作し、を呼び出すたびにXMLドキュメントの次のノードを提供する単純なXMLパーサーがあるとしますXMLParser.getNextNode()。私は必然的に次のようなコードを書くことに気づきます。

boolean insideBookNode = false;
boolean insideTitleNode = false;

while (!XMLParser.finished())
{
    ....
    XMLNode n = XMLParser.getNextNode();

    if (n.type() == XMLTextNode)
    {
        if (insideBookNode && insideTitleNode)
        {
            // We have a book title, so do something with it
        }
    }
    else
    {
        if (n.type() == XMLStartTag)
        {
            if (n.name().equals("book")) insideBookNode = true
            else if (n.name().equals("title")) insideTitleNode = true;
        }
        else if (n.type() == XMLEndTag)
        {
            if (n.name().equals("book")) insideBookNode = false;
            else if (n.name().equals("title")) insideTitleNode = false;
        }
    }
}

基本的に、XML処理はすぐに巨大なステートマシンドリブンループに変わり、以前に見つけた親ノードを示すために多くの状態変数が使用されます。それ以外の場合は、ネストされたすべてのタグを追跡するために、スタックオブジェクトを維持する必要があります。これはすぐにエラーが発生しやすく、保守が困難になります。

繰り返しますが、問題は、関心のあるデータが個々のノードに直接関連付けられていないことです。もちろん、XMLを次のように記述した場合、可能性があります。

<book title="Blah blah" author="blah blah" price="15 USD" />

...しかし、これはXMLが実際にどのように使用されるかということはめったにありません。ほとんどの場合、親ノードの子としてテキストノードがあり、テキストノードが何を参照しているかを判断するために、親ノードを追跡する必要があります。

だから...私は何か間違っていますか?もっと良い方法はありますか?どの時点でXMLストリームベースのパーサーを使用するのが面倒になり、完全なDOMパーサーが必要になりますか?他のプログラマーから、ストリームベースのパーサーでXMLを処理する際にどのようなイディオムを使用するかを聞きたいです。ストリームベースのXML解析は常に巨大なステートマシンに変換する必要がありますか?


2
.net言語を使用している場合は、linq to xml別名XLinqを確認する必要があります。
ムアディブ

ありがとう、私はこの問題を抱えている唯一の人だと思った。率直に言って、私はしばしばXML形式全体が助けというよりも障害であると感じています。はい、多くの構造化データを小さなテキストファイルに保存できます。ただし、20以上のクラスが必要な場合は、それを展開して意味を理解してください。多かれ少なかれ重要なものを見落としていないという保証はありません。モンティパイソンの聖杯にいるウサギのようなものです。
エリーゼヴァンルーイイ

回答:


9

私にとっては、問題は逆です。XMLドキュメントはどの時点で面倒になり、DOMではなくSAXの使用を開始する必要がありますか?

SAXを使用するのは、非常に大きな不定サイズのデータ​​ストリームに対してのみです。または、XMLが呼び出すことを意図した動作が実際にイベント駆動型であるため、SAXに似ている場合。

あなたが与える例は、私にとって非常にDOMに似ています。

  1. XMLをロードする
  2. タイトルノードを抽出し、「それらで何かをする」。

編集:不正な形式の可能性のあるストリームにもSAXを使用しますが、データを取り出すのに最善の推測をしたいところです。


2
これは良い点だと思います。あなたはDOMのために大きすぎる文書を解析しているなら、あなたはどうかはあまりにも大きいです、あなたしている解析文書を検討する必要があるXML
ディーン・ハーディング

1
+1:オプションがあれば、私はいつも DOMを使います。残念ながら、設計要件には常に「あらゆるサイズのドキュメントを処理する能力」と「パフォーマンスが必要」が含まれており、DOMベースのソリューションはほとんど除外されているようです。
TMN

3
@TMN、理想的には、要件がそもそもXMLを排除するという理想的な世界です。
SKロジック

1
@TMN、それはそれらの幻の要件の1つのように聞こえます:「もちろん、私たちの文書はすべて約100KBであり、私たちが見た最大のものは1MBですと無限に大きなドキュメントのために構築」
ポール・ブッチャー

@ポール・ブッチャー、あなたは決して知らない。つまり、Wikipediaのダンプは30GBのXMLのようなものです。
Channel72

7

私はXMLをあまり扱いませんが、私の意見では少しですが、おそらくライブラリでXMLを解析する最良の方法の1つはXPathを使用することです。

ツリーを走査して特定のノードを見つける代わりに、そのパスを指定します。あなたの例の場合(擬似コードで)、それは次のようなものになるでしょう:

books = parent.xpath( "/ book")//これはすべての本ノードを提供します
書籍ごとの本
    title = book.xpath( "/ title / text()")
    author = book.xpath( "/ author / text()")
    価格= book.xpath( "/ price / text()")

    //データを処理します

XPathはそれよりはるかに強力で、条件(値と属性の両方)を使用して検索し、リスト内の特定のノードを選択し、ツリー内でレベルを移動できます。使用方法に関する情報を探すことをお勧めします。多くの解析ライブラリに実装されています(Pythonの.Net Frameworkバージョンとlxmlを使用しています)


xmlが構造化されている方法を事前に知って信頼できる場合は、それで問題ありません。たとえば、要素の幅をノードの属性として指定するのか、要素のサイズノード内の属性ノードとして指定するのかわからない場合、XPathはあまり役に立ちません。
エリーゼヴァンルーイイ

5

ストリームベースのXML解析は常に巨大なステートマシンに変換する必要がありますか?

通常、そうです。

本格的なDOMパーサーを使用することを指すのは、たとえば、ドキュメント内の相互参照を解決できるようにするために、メモリ内のファイル階層の一部を模倣する必要がある場合です。


+1:DOMから始めます。SAXを避けてください。
-S.Lott

またはvtd-xml
vtd-xml-author

4

解析は一般に状態マシンを駆動するだけであり、XML解析も同様です。ストリームベースの解析は常に面倒です。先祖ノードを追跡するために何らかのスタックを構築し、多くのイベントと、タグまたはパスレジストリをチェックしてイベントを起動するイベントディスパッチャを常に定義します。一致する場合。コアコードはかなりタイトですが、ほとんどの場合、次のテキストノードの値をどこかの構造体のフィールドに割り当てることで構成されるイベントハンドラーの巨大な塊になります。そこにビジネスロジックを混在させる必要がある場合は、かなりむずかしくなります。

サイズまたはパフォーマンスの問題が特に指示されていない限り、私は常にDOMを使用します。


1

言語に完全に依存しているわけではありませんが、私は通常、構文解析について考えるよりも、XMLをオブジェクトにデシリアライズします。速度に問題がある場合は、構文解析戦略自体を心配するだけです。


それは構文解析に該当します。問題のXMLがオブジェクトのシリアル化の出力であり、既製の逆シリアル化ライブラリがある場合を除きます。しかし、この質問は表示されません。

多くの言語/スタックには、デシリアライゼーションライブラリがビルドされています。
ワイアットバーネット

うん、それで何?私の要点は今も変わりません-野生のすべてのXMLファイルがそのような形式であるわけではありません。もし持っているのであれば、このデシリアライゼーションライブラリを使用して自分で何も解析しないので、この質問はしません。ストリームなどから。

0

XPathを使用できれば、面倒な作業が少なくなります。そして、.Netの土地では、LINQ to XMLがあまり魅力的でないものを抽象化します。(編集 -これらはもちろんDOMアプローチが必要です)

基本的に、ストリームベースのアプローチを取っている場合(したがって、DOMを必要とするより優れた抽象化を使用することはできません)私はそれは常にかなり面倒だと思うし、これを回避する方法があるかどうかはわかりません。


XPathを使用している場合は、DOMを使用しています(自家製のXPathエバリュエーターで使用している場合を除く)。
TMN

はい、したがって、DOMを必要とする抽象化についての私のコメントは...ですが、明確にします、ありがとう!
スティーブ

0

イテレータを提供するパーサーを見つけることができる場合、それをレクサーとして扱い、ステートマシンジェネレーターを使用することを考えましたか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.