SSブログ

光学薄膜設計ソフトの設計 その36 - NSXMLDocumentの使い方 [考え中 - 光学薄膜設計]

Cocoa Break!さんのリンク集に取り上げてもらってうれしいので今回から何日かCocoaのコードについて書こう。単純。

ところでCocoa Break!さんにはずっとお世話になっている。Appleの抽象的な記述の多い、決して読みやすいとは言えないGuideは、Cocoa Break!さんの翻訳を読ませてもらって「こんなことを言ってたのか」と初めて理解できるということがよくある。

僕のブログにAppleのGuideやReferenceへのリンクを張ることがあるけど、これはやはり第1ソースを参照することにしているのでどうしても仕方がない(本やCDの評論のページのリンク先がAmazonになっているのとかは、僕はやりたくない)。日本語を母国語にしている人でGuideやReferenceへのリンクをたどって先を見る気がある人は、それに対応するCocoa Break!さんの翻訳を見た方が手っ取り早い。日本語を母国語にしていない人がこのブログを見ることはないだろうと思うけど。

ということで、お世話になってます。

XMLを解析するための方法

一般的にはXMLファイルの解析に二通りのやりかたが用意されている。それは

  • 木構造ベース型
  • イベント駆動型
で、木構造ベース型とはファイル全部を読み込んでそれに対応する木構造データを内部的に作ってしまってから、それをあとからたどって必要な情報を得る方法でDOM(Document Object Model)としてAPIが標準化されている。もうひとつのイベント駆動型は読み込みながら要素に出会った、その子要素に出会った、と次々報告させる方法で、こっちは標準化されているわけではないけどSAX(Simple API for XML)がデファクトになっている。どちらも使い方を定めただけの抽象的なAPIで、実装はいろいろあるらしい。また、Cのライブラリとして標準的なexpatもイベント駆動型である。

当然、木構造ベース型はメモリを食うけど構造が出来上がってから中身を調べるので、編集してもう一度ファイルに書き出したりできる。イベント駆動型は軽いけど、パース動作をいわゆるコールバック関数(Javaならインベントハンドラ、Cocoaならdelegate)として実装する必要があって「あなたに合わせます」型に書かなければいけなくて面倒なのと、読みっぱなしで、後戻りできない。どちらかというとイベント駆動型のほうが多く使われている感じがするけど、その場合スタックを明示的に使ったりするテクニカルなコードが目につく気もする。

XMLを解析するCocoaクラス

Cocoaには両方の方式に対応するクラスがそれぞれFoundationにある。木構造ベース型はNSXMLDocumentで、イベント駆動型はNSXMLParserでそれぞれパースできる。Cocoaの場合、NSXMLDocumentそのものはNSXMLParserを使って作られているらしい。まあ、そらそうだわな。両方書けと言われてそれぞれ別物として書くプログラマがいたとしたら、そいつのコードは信用できんわ。

木構造ベース型のクラスはNSXMLDocumentだけでなく、いくつかのクラスからできていて

  1. NSXMLNode
  2. NSXMLElement
  3. NSXMLDocument
  4. NSXMLDTDNode
  5. NSXMLDTD
があるけど、NSXMLNode以外はすべてNSXMLNodeのサブクラスになっている。

もう一方のイベント駆動型は

  1. NSXMLParser
のひとつだけ。このインスタンスのデリゲートとして処理を実装する。NSXMLParserが、たとえばXML要素に出会うたびにparser:didStartElement:namespaceURI:qualifiedName:attributes:が呼ばれるのでその中に処理を書く、というスタイルになる。エラーの報告もdelegate methodを通じて行われる。

NSXMLDocument

今回はそれほど大きなデータ構造にはならないので、NSXMLDocumentを使って読み込んでしまって、そのあとじっくり中身を解析する、と言う方法にする。

NSXMLDocumentの初期化はおもに

- (id)initWithContentsOfURL:(NSURL *)url
                    options:(NSUInteger)mask
                      error:(NSError **)error;
- (id)initWithData:(NSData *)data
           options:(NSUInteger)mask
             error:(NSError **)error;
- (id)initWithXMLString:(NSString *)string
                options:(NSUInteger)mask
                  error:(NSError **)error;
のみっつがある(それ以外に既存のNSXMLElementからつくることもできる)。メソッド名を見ればわかるようにひとつ目はURLを指定してそこから、ふたつ目はNSDataから、みっつ目はNSStringからで、例えば
- (id)initWithContentsOfURL:(NSURL *)url
                    options:(NSUInteger)mask
                      error:(NSError **)error
{
    NSData  *xmlData = [NSData dataWithContentsOfURL:url
                                             options:NSUncachedRead
                                               error:error];
    if (!(*error))
        return nil;
    return [self initWithData:xmlData options:mask error:error];
}
などとできるので実質的におなじである。

とりあえず、読み込みとは分けて、そのうえでエンコードの問題などを避けるためにふたつ目のNSDataから作るのが簡単。

初期化のときのmaskは、ドキュメントには

NSXMLDocumentTidyHTML = 1 << 9,
NSXMLDocumentTidyXML = 1 << 10,
NSXMLDocumentValidate = 1 << 13,
NSXMLDocumentXInclude = 1 << 16,
NSXMLDocumentIncludeContentTypeDeclaration = 1 << 18,
となっている。今回関係するmaskは、まずNSXMLDocumentTidyXMLで、これは構文解析エラーが起こるような場合(タグが閉じてないとか)を回復しようと努力をした上で読み込む。それとNSXMLDocumentValidateはDTDか、XML Schemaが指定されていると(DTDの場合はファイル内部に書かれている場合と、外のファイルを参照する場合の両方で)XMLがValidかどうか検証する。DTD、XML Schemaの記述からはずれているとNSXMLDocumentのオブジェクトは作られず、errorに検証内容が入って返される。

今回、内部DTDの場合は動作が確認できたけど、XML SchemaはどうしてもSchemaファイルを読んでくれない。

AppleのTree-Based XML Programing GuideCocoa Break!さんのサイトに「Cocoa のための木構造ベースの XML プログラミングガイド」として翻訳あり)に「 A schema should be physically installed on a Mac OS X system at /System/Library/Schemas/elementName.xsd. 」とあって、Schemaファイルの位置が決めうちになっているように書いてあるけど、ここに置いてもやっぱり読んでくれない。

よくわからない。Schemaファイルの記述の仕方が間違っている可能性が高いけど、それがチェックできない。NSXMLDocumentもそれを報告してくれない。

今回の場合、データを記述するのでなるべく厳密にしておきたい。ということでNSXMLDocumentTidyXMLのフラグはオフにしてNSXMLDocumentValidateをオンにする。ただし、これでもDTDのレベルの検証なのでオブジェクトの中身の検証(例えば数字があるべきところに文字が入っているとか)は自前でやる必要がある。XML Schemaならこのレベルも検証可能だったはず。

NSXMLDocumentValidateの検証は、コンパイラのようにできるだけたくさんのエラーを報告してくれるわけではなくて、最初に出会った時点でパースを中止するみたい。複数個のエラーが含まれているときは、ひとつ修正しては読み込んで、という繰り返しが必要になる。

次回は読み込みが成功したNSXMLDocumentの中身をたどる方法について。


nice!(0)  コメント(2)  トラックバック(0) 

nice! 0

コメント 2

a310

明快に解説していただき大変感謝しています。いろいろ不確かな認識があったのですが整理がつけられました。
NSXMLDocumentValidateに外部のXML Schema を参照させる方法ですが、
xmlns:xsi= と xsi:schemaLocation= アトリビュートをルートエレメントに付け加えたら、一応読みにいくようになりました。/System/...ではなく、xsi:schemaLocationにhttp://...でネット上のSchemaを指定してみました。
by a310 (2010-03-23 01:03) 

decafish

コメントありがとうございます。
お役に立てたとしたら、僕としてもうれしいです。
Schemaの読み込みは今度試してみます。
なにしろ、すっかり忘れてしまっていて...
何のためのメモなんだか....
by decafish (2010-03-23 21:45) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立03/22献立03/24 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。