XML を扱う

Ruby で REXML を使って XML を扱うこともできるけど、ここでは、NSXMLDocument を使って XML 文書を扱う方法を試してみる。

とりあえず、読み込むことから始めてみる。ただ、XML の基本をよくわかっていないので、試したことのメモから。あとで、理解が深まって時間があればまとめ直すつもり。

XML ファイルを読み込む

まずは、XML ファイルから読み込んで NSXMLDocument オブジェクトを作る。これには、次の3つが使える。

    • initWithContentsOfURL_options_error(url,mask,error)

    • initWithData_options_error(data,mask,error)

    • initWithXMLString_options_error(string,mask,error)

initWithContentsOfURL_options_error(url,mask,error) は NSURL オブジェクトで URL を指定して読み込む。ただ、XML のヘッダがないと、文字コードを正しく読み込んでくれない場合があるので使えない(1バイトの ASCII 文字だけならいいかも)。initWithData_options_error(data,mask,error) は NSData オブジェクトから NSXMLDocument オブジェクトを作る。これは、NSData オブジェクトが既にある場合には使えるけど、それ以外だと、ファイルを NSData に読み込んで、となって、結局はヘッダがないと面倒なことになる。initWithXMLString_options_error(string,mask,error) は、NSString オブジェクトから XMLDocument オブジェクトを作るんだけど、ファイルから NSString オブジェクトを作るときに文字コードが指定できるので、元のファイルの文字コードがわかっていればとりあえずは読み込むことができる。それぞれに得手不得手があるので、とりあえず、いろいろ試して読み込んでみることから始める。

まずは、ちゃんと XML のヘッダがある場合。ホームの書類フォルダにある test.xml というファイルを読み込む。

url = NSURL.fileURLWithPath(NSHomeDirectory()+"/Documents/test.xml")

xmlDocument = NSXMLDocument.alloc.initWithContentsOfURL_options_error(url,NSXMLDocumentTidyXML,nil)

ここでは、url という NSURL オブジェクトを作っている。OS X のホームフォルダのパスは OSX.NSHomeDIrecotry で得られるので、それに Documents/test.xml を追加している。これを Open パネルを使ってファイルを読み込む場合は、url を取り出しておく。option の mask は NSXMLDocumentTidyXML にしておく。NSXMLDocumentTidyHTML でもいいんだけど、これは XHTML 文書を読み込む場合、HTML 文書を XHTML 文書形式にそった形に整形して処理する時に使う。つまり、タグがなくても、XHTML 文書と見なして、適当なタグをつけてから処理にまわす。ここは、XML 文書なので前者を使う。

次は、XML のヘッダがないか、あるものとないものが混じっている場合。これは一番最後の方法で読み込む。ここでは UTF-8 の決めうちだけど、ファイルの文字コードにあったものに変えるか、Open パネルを使うなら、文字コードの選択ができるようにしておく。ここでも同じようにホームフォルダの書類フォルダにある test.xml というファイルを読み込む

filepath = NSHomeDirectory()+"/Documents/test.xml"

content = NSString.alloc.initWithContentsOfFile_encoding_error(filepath,NSUTF8StringEncoding,nil)

xmlDocument = NSXMLDocument.alloc.initWithXMLString_options_error(content,NSXMLDocumentTidyXML,nil)

この方法だと、ヘッダがなくても、NSString オブジェクトとして読み込まれるときに文字コードが指定できるので、そこでちゃんと読み込めてさえいれば、NSXMLDocumentTidyXML を指定してちゃんとした XML 文書として整形して処理するので問題が少ない。

さて、これで XML の文書だと認識される NSXMLDocument オブジェクトができた。

XML 文書中の情報を得る

次に、XML 文書の中身にアクセスしていく。例として、こんな感じの簡単な XML 文書にアクセスする前提で進める。

<?xml version="1.0" encoding="UTF-8"?>

<text type="newspaper">

<info>

<section>sports</section>

<title></title>

</info>

</text>

まずは、ルート要素にアクセスする。

node = xmlDocument.rootElement

で、これがすべての要素を取り出すスクリプト(のはず)。とりあえず試行錯誤の結果、今のところはこんな感じ。いくつかのファイルで試したけど、それなりにちゃんとできてる。ただ、複雑な構造だとどうなるか全然わからない。

def repeatSearch(child)

hash = Hash.new

ary = Array.new

if child.is_a?(NSXMLElement) && child.attributes.nil? == false

hash['attributes'] = child.attributes.map{|x| [x.name,x.objectValue]}

elsif child.is_a?(NSXMLElement) && child.childCount == 1

hash['text'] = child.objectValue.to_s.gsub(/\s+/," ")

end

hash['name'] = child.name if not child.name.nil?

hash['parent'] = child.parent.name if not child.childCount == 0

ary << hash if hash != {}

aryChildren = Array.new

if child.childCount >= 1

child.children.each do |chi|

hash['level'] = chi.level

ary << repeatSearch(chi)

hash['text'] = child.objectValue.to_s.gsub(/\s+/," ") if chi.is_a?(NSXMLElement) == false

end

end

return ary.flatten

end

xmlAry = Array.new

xmlAry << repeatSearch(node)

xmlAry.flatten!