XMLReaderを使ってオントロジーのクラス数を数える(XMLReader初体験!)

オントロジー(OWLファイル)のクラス数を数えてみようと思ったが,SimpleXMLだと読み込まれなくて困った.
代替案として,PHP5以降で使えるXMLReaderクラスを使ってみることにした.
本エントリーでは,XMLReader初体験の自分用メモとして,使い方などを書き残しておく.

  • -

オントロジー例(onto.owl)

<?xml version="1.0"?>
<rdf:RDF 
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns="http://www.owl-ontologies.com/Ontology1000000000.owl#"
    xml:base="http://www.owl-ontologies.com/Ontology1000000000.owl">
  <owl:Class rdf:ID="Class_B">
    <rdfs:subClassOf>
      <owl:Class rdf:ID="Class_A"/>
    </rdfs:subClassOf>
  </owl:Class>
  <owl:Class rdf:ID="Class_D">
    <rdfs:subClassOf>
      <owl:Class rdf:ID="Class_C"/>
    </rdfs:subClassOf>
  </owl:Class>
</rdf:RDF>

RDF Gravityで可視化するとこんな感じ.

  • -

まず,SimpleXMLで読み込めなかった状況のみを載せておく.
下のように,普通に出来ると思いきや,何も出力されなかったので,諦めたって話w

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

(2008.09.16追記)
SimpleXMLでも読み込めることが判明.
SimpleXMLでもオントロジーを読み込めた!(childrenで解決) - kakku blog
SimpleXMLで名前空間付きタグの一覧を取得する - 肉とご飯と甘いもの @ sotarok

  • -

ここからXMLReaderの話.
とりあえず,使ってみる.
読み込む.owlファイルは上に載せたものとする.

<?php
	$reader = new XMLReader();
	$reader->open("./onto.owl");
	
	while ($reader->read()){
		echo $reader->name;
		echo "<br />";
	}
	
	$reader->close();
?>

こんな感じに出力される.

rdf:RDF
#text
owl:Class
#text
rdfs:subClassOf
#text
owl:Class
#text
rdfs:subClassOf
#text
owl:Class
#text
owl:Class
#text
rdfs:subClassOf
#text
owl:Class
#text
rdfs:subClassOf
#text
owl:Class
#text
rdf:RDF

この「#text」っていうのは,改行やらタブやらを表す.
この#textを除去するには,以下のように「XMLReader::SIGNIFICANT_WHITESPACE」の判定をすればOK.

<?php
	$reader = new XMLReader();
	$reader->open("./onto.owl");
	
	while ($reader->read()){
		if($reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE){
			continue;
		}else{
			echo $reader->name;
			echo "<br />";
		}
	}

	$reader->close();
?>

綺麗になった.

rdf:RDF
owl:Class
rdfs:subClassOf
owl:Class
rdfs:subClassOf
owl:Class
owl:Class
rdfs:subClassOf
owl:Class
rdfs:subClassOf
owl:Class
rdf:RDF
  • -

次は「XMLReader::getAttribute」を使って,要素内容を抽出してみる.
getAttributeの引数に,抽出したい要素内容の要素名を指定する.接頭辞ありでもそのまま記述可能(XMLReader::getAttributeNsを使ってもOK).

<?php
	$reader = new XMLReader();
	$reader->open("./onto.owl");
	
	while($reader->read()){
		if($reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE){
			continue;
		}else{
			echo $reader->getAttribute("rdf:ID");
			echo "<br />";
		}
	}
	
	$reader->close();
?>

以下の感じに出力される(改行が入るがここでは省略).

Class_B
Class_A
Class_B
Class_D
Class_C
Class_D
  • -

onto.owlの総クラス数は4なのに,上の出力では6になっている.
見たところ,閉じの要素名でも要素内容が抽出されてしまっているのが原因のよう.
閉じの要素名では,カウントしないようにするためには,「XMLReader::END_ELEMENT」を判定する.

<?php
	$reader = new XMLReader();
	$reader->open("./onto.owl");
	
	while($reader->read()){
		if($reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE || $reader->nodeType == XMLReader::END_ELEMENT){
			continue;
		}else{
			echo $reader->getAttribute("rdf:ID");
			echo "<br />";
		}
	}
	
	$reader->close();
?>

これで正しいクラスの総数が抽出できた(改行が入るがここでは省略).

Class_B
Class_A
Class_D
Class_C
  • -

後はこの出力の総数を数えればいいだけなので,簡単に.

<?php
	$reader = new XMLReader();
	$reader->open("./onto.owl");
	
	while($reader->read()){
		if($reader->nodeType == XMLReader::SIGNIFICANT_WHITESPACE || $reader->nodeType == XMLReader::END_ELEMENT){
			continue;
		}else{
			if($reader->getAttribute("rdf:ID")){
				$num++;
			}
		}
	}
	$reader->close();
	echo $num;
?>

できたー!

  • -

そんなことで,オントロジーのクラス数を数えるために,XMLReaderを初めて使ってみた.
XMLReaderは,まずデータの階層を全て読み込むSimpleXMLとは違って,イテレータのような感じで1行1行処理をしていくことが出来る点が特徴.
なのでファイルが大きくてメモリを食いすぎる場合などに有効.
今回読み込むオントロジーファイルは結構大きいので,XMLReaderで良かったと思う.

  • -

関連エントリー:
PHP: XMLReader - Manual
PHP で XML をプル型構文解析する - IBM developerWorks Japan
SimpleXMLとXMLReaderのまとめ(PHP勉強会で話してきたコード) - 肉とご飯と甘いもの @ sotarok
第34回PHP勉強会で話してきました - maru.cc@はてな