REXML 昨日の続き
昨日のエントリで「REXMLを使うときにはクラスに注意しましょう」みたいなことを書いたが、基本は
require 'kconv' require 'rexml/document' include REXML doc = REXML::Document.new File.open("newsing.xml","r") p doc.class #=> REXML::Document p doc.root.class #=> REXML::Element
でREXML::Documentクラスを生成した後、rootメソッドをつけてDOMツリーの一番上から要素を指定してやればよいみたい。
RubyでXML操作 | Netsphere Laboratories
例
XMLのサンプルは昨日の日記参照。…データがちょっと違うけど構成は一緒なのでまぁ察してください(´ー`)
puts doc.root.elements[1].elements[1].class #=> REXML::Element puts doc.root.elements[1].elements[1].text.tosjis #=> http://newsing.jp/entry?url=www.shopjapan.co.jp%2Fbillysbootcamp%2F%3Faf_id%3D23%26banner_id%3Dya_00164 puts doc.root.elements[1].elements[2].text.tosjis #=> うわさのビリーはよく見ると " ET " みたい puts doc.root.elements[1].elements[3].text.tosjis #=> undefined method `tosjis' for nil:NilClass (NoMethodError)
空要素の時はエラーとなるので条件分岐を入れなければいけない。
p doc.root.elements[1].elements[3].has_text? #=> false puts doc.root.elements[1].elements[3].text.tosjis if doc.root.elements[1].elements[3].has_text? #=> 何も出力されない
ちなみにREXML::Elements#[index, name=nil]という形で「nameという要素名を持つindex番目の要素」を返せる。
が、今回使うXMLのように全ての要素名がユニークな時には使う意味はない。逆に
<?xml version="1.0" encoding="Shift-JIS" standalone="yes"?> <items> <item> <url>http://newsing.jp/entry?url=www.shopjapan.co.jp%2Fbillysbootcamp%2F%3Faf_id%3D23%26banner_id%3Dya_00164</url> <title>うわさのビリーはよく見ると " ET " みたい</title> <comment>コメント1</comment> <comment>コメント2</comment> <comment>コメント3</comment> <comment>コメント4</comment> </item> </items>
みたいなフラットなXMLなら「X番目のコメントを取り出す」とかに使えて便利。
XPATHでやる
以上がindex番号による指定で、当然XPATHでも指定できる。
puts doc.root.elements[1].elements["url"].text.tosjis #=> http://newsing.jp/entry?url=www.shopjapan.co.jp%2Fbillysbootcamp%2F%3Faf_id%3D23%26banner_id%3Dya_00164 puts doc.root.elements[1].elements["title"].text.tosjis #=> うわさのビリーはよく見ると " ET " みたい puts doc.root.elements[1].elements["main_text"].text.tosjis #=> undefined method `tosjis' for nil:NilClass (NoMethodError)
後から要素が追加される可能性を考えると、こっちの方が賢そう。
指定した要素全てを抜き出す
each(ブロック)を使わずXMLの指定した要素を全て取り出す方法はいろいろ調べたけどないっぽい。残念。
REXML::Elements#[xpath] xpathに最初にマッチした子要素を返す。
XPATHで見つかった要素全てを配列で返してくれるとかだったら良かったのに。
仕方がないのでブロックで
require 'kconv' require 'rexml/document' include REXML doc = REXML::Document.new File.open("newsing.xml","r") # 1.基本形 doc.root.each_element do |elem| puts elem.elements["title"].text.tosjis end # 2.回数を指定する場合 doc.root.each_element_with_text(nil, 10) do |elem| puts elem.elements["title"].text.tosjis end # 3.XPATHでダイレクト指定 doc.elements.each("//title") do |elem| puts elem.text.tosjis end # 4.uptoでごり押し 1.upto(10) do |i| entry = doc.root.elements[i] puts entry.elements["title"].text.tosjis end
結果は全部同じ。#each_element_with_textが回数指定できて、#each_elementはできないってのが疑問だけど仕方ない。それぞれの方法に得手不得手があるから目的に合わせて使い分ければいいんじゃないかな(・ω・)人(・ω・)
textとget_text
get_textメソッドを使うとREXML::Textクラスで抽出し、特殊文字を実体参照に変換してくれる。
require 'kconv' require 'rexml/document' include REXML doc = REXML::Document.new File.open("newsing.xml","r") puts doc.root.elements["//title"].text.class #=> String puts doc.root.elements["//title"].text.tosjis #=> うわさのビリーはよく見ると " ET " みたい puts doc.root.elements["//title"].get_text.class #=> REXML::Text puts doc.root.elements["//title"].get_text.to_s.tosjis #=> うわさのビリーはよく見ると " ET " みたい puts doc.root.elements["//title"].get_text.tosjis #=> undefined method `tosjis' for #<REXML::Text:0x2ba54d0> (NoMethodError)
普通にエスケープすればよいのであまり使わないかな…。