[PHP] XML操作のxpathでのハマりどころ

Pocket
LINEで送る
GREE にシェア
LinkedIn にシェア

とあるサービス構築構築をしている時に、上場会社の財務情報を取得するために、XBRLというフォーマットを扱う必要があったのですが、色々な場面で使われているXBRLは中身はXMLとHTMLの混合の仕様になっており、これらをPHPで扱う時には、XML操作が必須になります。
 

xmlはタグや属性を自由に登録できるため、かなり汎用的なデータベースとしても利用が可能なため、結構古くからWEBでやり取りするデータとしては使われていて、PHPもsimpleXMLという内部モジュールで、内部データを扱うことが可能になっています。
 

WEB系エンジニアの人は、SQLとjsonが扱いやすいデータ構造だと思いますが、xmlもそんなに大差ないデータベースではあるため、苦手感を持たずにデータアクセスをしてみたところ、非常に初歩で躓きポイントがあったので、その対応方法も含めてブログに掲載しておきます。

下位検索が全検索になってしまう問題

一つのXMLから任意のグループ別に情報を取得してそれぞれのグループ毎にデータを抽出したくなる場合の時に、まずxpathで対象のグループ一覧を取得し、配列に保存し、forかforeachでそれぞれのグループ毎にさらにxpathを行う際に、何故かグループ内ではなく、全体から検索してしまうという、不具合に近い症状が発生します。
 

具体的なデータと内容と下記に載せておきます。
 

 

 

 

「bbb > ccc」という値をそれぞれ取得しようとしたわけですが、思った通りにxpathを書いて見たところ、2つとも同じ結果が表示されてしまいました。
 

ちなみに、xpathの中に記述している「//***」は、内部検索をするというような仕様であり、bbbタグを取得して2つの配列が生成され、それを$nodeに入れ込んだforeachを実行しているのですが、「$node->xpath(“//ccc”)」が$nodeの上位階層から検索されているというのが、普通に考えるとなかなか癖の強い関数だと思われます。
 

xpath操作で四苦八苦

ダイレクトアクセス

 

 

直接階層指定をしたところ、想定した結果を得ることができましたが、これでは、汎用的なデータ取得がやりずらいので、このやり方は却下なのですが、bbbを配列で指定しているところがミソなのですが、開始番号が1からという点が、プログラマーの人たちが眉をしかめるポイントではないでしょうか?
 

awk言語の配列も確か1スタートだったので、「これはコレ!」と思って取り組むしかないでしょう。
 

検索指定ではないタグ指定

 

 

今度も同じ結果になりましたが、これは、「$node->xpath(“ccc”)」という風に、「//***」という記述ではなく、タグのみを書いて見たところ、どうやら下位層を拾うことができるようです。
 

ただし、このやり方はcccが検索されたグループのrootになっていないといけないようです。
 

cccの下にdddが存在してそこにアクセスしたい場合は、「$node->xpath(“ccc/ddd”)」と書かなくては行けなくて、「$node->xpath(“ddd”)」という風に書くと、ブランクが返ってきてしまいます。
 

このやり方も階層が固定であればいいのですが、できれば、下位層検索になる方式で行いたいので、このやり方も却下です。

解決方法

このやり方を解決するには、root階層を切り替える必要があるので、以下のようなコードを書くことで想定の結果が得られました。
 

 

 

ちゃんと、グループ階層の下位層が検索されています。
rootにcccが存在しなくても検索しに行ってくれます。
 

簡単解説

ポイントは「$node = simplexml_load_string($node->asXML());」の行で、$nodeを一度「asXML()」でxml文字列に切り替えてそれを「simplexml_load_string()」でxmlオブジェクト化して上書きしています。(別に上書きしなくてもいいのですが・・・)
 

こうすることで、オブジェクトがその階層しか存在しなくなるので、検索範囲が特定されるということなんですね。
 

なんじゃこりゃ!
 

普通にプログラミングしていて、「これはないやろ〜〜〜!」ていう感じでしたが、歯を食いしばって我慢しましょう。
 

それにしても、これってphpのxpathの症状なのでしょうか?ホントにバグにしか考えられないんですけどね。
どんな仕様やねん!!

Leave a Reply

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です