読者です 読者をやめる 読者になる 読者になる

肉とビールとパンケーキ by @sotarok

少し大人になった「肉とご飯と甘いもの」

SimpleXMLで名前空間付きタグの一覧を取得する

PHP

via. http://d.hatena.ne.jp/kakku22/20080914/1221366973

まず,SimpleXMLで読み込めなかった状況のみを載せておく.

下のように,普通に出来ると思いきや,何も出力されなかったので,諦めたって話w

<?php
	$xml = simplexml_load_file("./onto.owl");
	print_r($xml);
XMLReaderを使ってオントロジーのクラス数を数える(XMLReader初体験!) - kakakakakku blog

実際は、

<?php
$xml = simplexml_load_file("./onto.owl");
var_dump($xml);

の結果は、

object(SimpleXMLElement)#1 (0) {
}

となります。実は、XML自体はSimpleXMLElementとして読み込まれているのですが、アクセスは普通にはできないのです。なぜなら名前空間がバッチリついているからですね。

というわけで、名前空間付きのタグを取り出す方法の復習。

SimpleXMLで名前空間付き要素を取り出す

<?php
$xml = simplexml_load_file("./onto.owl");
$xml = $xml->children("http://www.w3.org/2002/07/owl#");
var_dump($xml);

結果は、

object(SimpleXMLElement)#2 (1) {
  ["Class"]=>
  array(2) {
    [0]=>
    object(SimpleXMLElement)#3 (0) {
    }
    [1]=>
    object(SimpleXMLElement)#4 (0) {
    }
  }
}

つまり、名前空間付きの子要素をとってくる場合、Childrenなどというクソわずらわしいものをつけなければいけません。これはSimpleXMLの仕様です。
で、id:kakku22 さんのやりたかったことをしようとしたら、

<?php

$xml = simplexml_load_file("./onto.owl");

$num = 0;
foreach ($xml->children("http://www.w3.org/2002/07/owl#") as $owl) {
    if ($owl->Class instanceof SimpleXMLElement) {
        $num++;
    }
}
echo $num;
echo "\n";

とかってやりますかね。
面倒だったのでSubClassは無視しちゃいましたが、まじめにやるなら再帰とか使ってサブツリーを掘っていくのが良いかと。

ところで、もっと簡単にやる方法が・・・XPath を使う

<?php

$xml = simplexml_load_file("./onto.owl");
echo count($xml->xpath("//owl:Class"));
echo "\n";

これで、

4

が得られます。

なぜかXPathでは名前空間付き要素が取れるんですね!不思議な不思議なPHP

で、もうちょっとまじめにがんばるなら、ちゃんと名前空間のPrefixを登録してあげます。(こうすれば、定義側で仕様変更があったときにも大丈夫です。ま、実際はほとんど必要ないとは思いますが・・・・)

<?php

$xml = simplexml_load_file("./onto.owl");
$xml->registerXPathNamespace("owl", "http://www.w3.org/2002/07/owl#");
echo count($xml->xpath("//owl:Class"));
echo "\n";

余談:名前空間付き属性へのアクセス

<?php

$xml = simplexml_load_file("./onto.owl");

foreach ($xml->children("http://www.w3.org/2002/07/owl#") as $owl) {
    if ($owl->Class instanceof SimpleXMLElement) {
        echo $owl->attributes("http://www.w3.org/1999/02/22-rdf-syntax-ns#")->ID;
        echo "\n";
    }   
}

追記

今回読み込むオントロジーファイルは結構大きいので,XMLReaderで良かったと思う.

大正解です。そのほうが良いと思いますw デカいファイルを読み込ませるととたんに破綻するのがSimpleXMLのかわいいところw
まぁ、一応、SimpleXMLでも要素は取得できるよ、という参考程度に。