XMLのルール

2009/09/23 | XML

知っているようで実はぜんぜん知らないXML。XML文書の中で、&が入るのは特別な場合を除いてエラーになることを今日はじめて知った。調べてみたらいろいろあるっぽい。知っていることも含めて列挙しておく。

  • ルート要素が必要。
    全ての要素を包含する一つのルート要素が必ず必要。
  • 終了タグが必ず必要。
    XHTMLでは当たり前だから、特に問題なし。
  • 要素は正しくネスト。
    これもXHTMLでは当たり前。 というか正しくネストしていないと気持ち悪くて。
  • 値を引用符で囲む。
    これも当たり前。
  • 大文字・小文字は区別。
    Linuxやってれば、これもぜんぜん気にならない。
  • エンティティ参照の宣言が必要。
    &と<を使うには一工夫必要。

6番目のが今回ぶつかった問題。例えば&amp;はOKだけど&copy;や&nbsp;はダメ。&は;他と組み合わせて、エンティティとして使うのだが、特別な場合を除いて、DTDで宣言してやらないといけない。特別な場合というのは&amp;、&lt;、$gt;、&quot;、&apos;。だから&copy;や&nbsp;をXML文書中で使用するのは間違いになる(どうしても使いたい場合はDTDで宣言しないといけない)。&単品で使用するのもダメ。必ず特別な場合の組み合わせのみで使用しないといけない。

いろいろ難しい。

simpleXMLのバグ

2009/09/19 | XML

今日気づいたことがある。これはsimpleXMLのバグでしょ。

simpleXMLElement->asXML(FILE_PATH)

上記のメソッドを実行すると、FILE_PATHにXMLを書き込んでくれることになっている。マニュアルによると書き込みに成功するとtrue、失敗するとfalseを返すことになっている。確かにマニュアルにはそう書いてある。

実際はそうはならない。書き込みに成功した場合、引数なしの場合と同様にXMLを返してきた。書き込みに失敗した場合、成功時と同じようにXMLを返してきた。おいおい。なぜだろう。ちょっと心配になって、最初試していたレンタルサーバとは別のレンタルサーバに同じファイルを設置して試してみた。やっぱり結果は同じ。

こんなバグがあるのか!、と思って調べてみたけど検索結果には出てこない。ひょっとして自分の書き方が間違っているのだろうか。それともバグの第一発見者かも?。

最初は以下のように記述していた。

if(!$xmlObj->asXML(FILE_NAME)){

しかし成否の判別ができないことがわかったので以下のように書き直すことにした。

if(!@file_put_contents(FILE_NAME,$xmlObj->asXML())){

・・・。ばかげてる。誰か追試してくれないかな。

simpleXMLはnamespaceに注意

2009/04/23 | XML

simpleXML関数が便利、ということを以前に確認した。これでウェブサービスを使ったサイト構築がずいぶんと簡単になった。しかし今日問題にぶち当たった。

「AmazonウェブサービスにRESTでリクエストを投げ、受け取ったXMLをsimpleXMLに放り込んで、XpathでXMLツリー構造内を検索して配列にする」ということをやりたかった。実際そのように記述してみたけど結果はNG。最後の配列が空になってしまう。なぜ。

$xmlObj =simplexml_load_string($xml);
$items=$xmlObj->xpath(‘//Item’);
var_dump($items);

楽天ではこんな感じで取得できた。楽天のときは、タグ名に「:(コロン)」があってうまく処理できずとまどったが、Xpathを使って切り抜けることができた(というかXMLをたどらなくていいのでこちらのほうが簡単)。今回も例にならってXpathをつかったけど、うまくいかなかった。理由はどうもnamespace(タグ内にxmlnsとかってかいてあるアレ)のようだ。楽天のときはnamespaceがなかったので問題が出なかっただけ。で、書き直した。

$xmlObj=simplexml_load_string($xml);
$nsArray=$xmlObj->getNamespaces();
$ns=array_shift($nsArray);
$xmlObj->registerXPathNamespace(‘amazonns’,$ns);
$items=$xmlObj->xpath(‘//amazonns:Item’);
var_dump($items);

Xpathを使用する際、namespaceが定義されている場合は、「namespaceはこれですよ」と登録しないといけないようだ(登録するときに適当に名前「amazonns」をつける)。Xpathで検索をかけるときは、その名前を使い、名前の配下のタグを検索させるような構造になるようだ。

とりあえず、これで結果を取得することはできた。しかし、やっぱりXMLの奥は深そうだ。

simplexmlでどうやって値を取得するか

2009/03/12 | XML

simplexmlでいろいろお試し中。現時点では「タグにコロンが含まれていた場合」の挙動を除けば快適に使えているのだが、少しだけ困った問題が起こった。問題自体は解決できたのだが、それに気づくまでちょっと時間がかかってしまったのと、スマートなやり方でないような気がしたのでちょっとメモ。

xml内の特定のタグでくくられた「値」を取得するのに、以下のように記述した。

$xmlObj =simplexml_load_string($result);
$status=$xmlObj->Header->Status;
var_dump($status);

個人的には’Success’とか’Error’とかの文字列を期待したのだが、出力された結果はこんな感じ。

object(SimpleXMLElement)#4 (1) { [0]=> string(7) “Success” }

オブジェクトが返された。まぁ、これはこれでいいのだけれど、このときに’Success’という文字列を取得する方法がわからなくて困った。「$status->0」なんてしてもやっぱり取得できない。でも、不思議なことに「print」すると「Success」と表示される。なぜ。

結論から言うと、strval($status)とすることで文字列を取得できた。これってスマートな方法なんだろうか。他に関数とか用意されていないのかな。後述の「printすれば」というとこをは理解できた。自動的に型変換されて出力されていたわけ(自動変換はいい場合もあれば、事を複雑にする可能性もあることを実感)。

ま、とりあえず、できた。

楽天APIのジャンル検索が面倒だったので

楽天APIで遊んでいる。今は商品検索APIを試用していて、キーワード検索の機能を使っているのだが、これだとキーワードにマッチするいろんな商品がひっかかってしまう(例えば「くつ」で検索した場合、シューズはもちろん、靴乾燥機や靴下も出てくる)のでジャンルを指定することにした。しかしこれ意外と面倒。ジャンルのリストが用意されているわけではなくて、いちいちAPIを使ってジャンルを検索していかないといけないのだ。はじめのうちはちょこちょこプログラムを書いてvar_dumpしつつデータを読み取っていたけれど、なんとも面倒なので汎用ジャンル検索ができるようプログラムを作ってみた。んでもって、誰でも使えるよう公開してみることにした。

楽天ジャンルID検索ツール

プログラムは単純でジャンル検索APIに準じてRESTを投げ、取得したXMLをsimplexml関数でデコードしている。自分のデベロッパーIDを使って公開すると、検索プログラムの負荷が高まって実際の商品検索ができなくなってしまうので、各自のデベロッパーIDを入力して使えるようにしてみた。自分で使ってみて・・・正直これは便利。他の人はどうしてるんだろ。自作して公開してる人もいるのではないだろうか・・・。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net