SimpleXMLモジュールを使ってAPIから取得したXMLを簡単に処理する

By fukumoto - 08/12/12 - このエントリをはてなブックマークに追加このエントリをYahoo!ブックマークに追加このエントリをdel.icio.usに追加このエントリをFC2ブックマークに追加

前回まではAPIのデータの取得について色々書いていましたが、
APIを利用すると大体はデータの受け渡しに適したXMLで返ってきます。
そのXMLの処理について今回は書きます。
 

PHPを使って処理することを前提として話を進めることになります。
PHP4まではそういった処理に、SAXやDOM_XMLモジュールが使われ、
本格的にXMLを使い込む場合には便利なものだったのですが、
APIなどを使う場合ほとんどデータの受け渡ししかしないので、
データを簡単にパースできればそれで十分。
ということで、その要望が実現されPHP5から新たに、
SimpleXMLモジュールというものが追加されました。
これを使うと簡単にデータをパースできます。
 

では実際に試してみましょう。
以前ご紹介した、Yahoo!ローカルサーチをつかって、
XMLでデータを取得してみます。
 

$word = '難波'; // 検索する駅を難波にしてみる
$word = mb_convert_encoding($word, 'UTF-8', 'auto');

// APIを使用するURLを生成
$url = 'http://map.yahooapis.jp/LocalSearchService/V1/LocalSearch?'
     . 'appid=*****************'
     . '&p=' . $word
     . '&category=station' // 駅名のみ検索するときのパラメータ
     . '&n=100'
     . '&datum=wgs'; 

//結果を取得
$contents = file_get_contents($url);
var_dump($contents);

 
結果を見てみましょう。
 

<LocalSearchResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:yahoo:jp:maps http://api.map.yahoo.co.jp/LocalSearchService/V1/LocalSearch/LocalSearchResponse.xsd">
<Count>3</Count>
<ViewCount>3</ViewCount>
<Query>難波</Query>
<Item>
<Category>Station</Category>

<Title>難波駅</Title>
<Address>大阪府大阪市浪速区難波中</Address>
<AddressLevel>0</AddressLevel>
<DatumTky97>
<Lat>34.66005533</Lat>
<Lon>135.50483289</Lon>

</DatumTky97>
<DatumWgs84>
<Lat>34.663312792164</Lat>
<Lon>135.50202380723</Lon>
</DatumWgs84>
<Url />
</Item>
<Item>

<Category>Station</Category>
<Title>近鉄難波駅</Title>
<Address>大阪府大阪市中央区難波</Address>
<AddressLevel>0</AddressLevel>
<DatumTky97>
<Lat>34.66394422</Lat>

<Lon>135.50194444</Lon>
</DatumTky97>
<DatumWgs84>
<Lat>34.667201194399</Lat>
<Lon>135.49913552432</Lon>
</DatumWgs84>
<Url />

</Item>
<Item>
<Category>Station</Category>
<Title>JR難波駅</Title>
<Address>大阪府大阪市浪速区湊町</Address>
<AddressLevel>0</AddressLevel>
<DatumTky97>

<Lat>34.66341578</Lat>
<Lon>135.49852756</Lon>
</DatumTky97>
<DatumWgs84>
<Lat>34.66667276344</Lat>
<Lon>135.49571895486</Lon>
</DatumWgs84>

<Url />
</Item>
</LocalSearchResult>

 
こういうデータがでてきます。
一見すると、複雑なXMLデータに見えますが、
よく見ると規則性のある簡単なXMLです。
XMLのツリーをもっと簡単にタグをプロパティ名とした、
PHPのオブジェクト(SimpleXMLElementオブジェクト)にマッピングできます。
 

$object = simplexml_load_string($contents);
var_dump($object);

 
中身は、このようになっています。
 

object(SimpleXMLElement)#48 (4) {
  ["Count"]=>
  string(1) "3"
  ["ViewCount"]=>
  string(1) "3"
  ["Query"]=>
  string(6) "難波"
  ["Item"]=>
  array(3) {
    [0]=>
    object(SimpleXMLElement)#49 (7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(9) "難波駅"
      ["Address"]=>
      string(36) "大阪府大阪市浪速区難波中"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#52 (2) {
        ["Lat"]=>
        string(11) "34.66005533"
        ["Lon"]=>
        string(12) "135.50483289"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#53 (2) {
        ["Lat"]=>
        string(15) "34.663312792164"
        ["Lon"]=>
        string(15) "135.50202380723"
      }
      ["Url"]=>
      object(SimpleXMLElement)#54 (0) {
      }
    }
    [1]=>
    object(SimpleXMLElement)#50 (7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(15) "近鉄難波駅"
      ["Address"]=>
      string(33) "大阪府大阪市中央区難波"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#55 (2) {
        ["Lat"]=>
        string(11) "34.66394422"
        ["Lon"]=>
        string(12) "135.50194444"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#56 (2) {
        ["Lat"]=>
        string(15) "34.667201194399"
        ["Lon"]=>
        string(15) "135.49913552432"
      }
      ["Url"]=>
      object(SimpleXMLElement)#57 (0) {
      }
    }
    [2]=>
    object(SimpleXMLElement)#51 (7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(15) "JR難波駅"
      ["Address"]=>
      string(33) "大阪府大阪市浪速区湊町"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#58 (2) {
        ["Lat"]=>
        string(11) "34.66341578"
        ["Lon"]=>
        string(12) "135.49852756"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#59 (2) {
        ["Lat"]=>
        string(14) "34.66667276344"
        ["Lon"]=>
        string(15) "135.49571895486"
      }
      ["Url"]=>
      object(SimpleXMLElement)#60 (0) {
      }
    }
  }
}

 
こういう風に使いやすくパースされます。
あとはこのオブジェクトを処理しやすい形に直します。
基本的にアロー演算子で下っていきます。
さらに今回は、取得した同系列のデータが複数ありますので、
ループも回しましょう。
 

for($i=0; $iItem); $i++){
    $data[$i]['station'] = (string)$object->Item[$i]->Title;
    $data[$i]['lat'] = $object->Item[$i]->DatumWgs84->Lat;
    $data[$i]['lon'] = $object->Item[$i]->DatumWgs84->Lon;
}
var_dump($data);

 
結果は、
 

array(3) {
  [0]=>
  array(3) {
    ["station"]=>
    string(9) "難波駅"
    ["lat"]=>
    object(SimpleXMLElement)#51 (1) {
      [0]=>
      string(15) "34.663312792164"
    }
    ["lon"]=>
    object(SimpleXMLElement)#50 (1) {
      [0]=>
      string(15) "135.50202380723"
    }
  }
  [1]=>
  array(3) {
    ["station"]=>
    string(15) "近鉄難波駅"
    ["lat"]=>
    object(SimpleXMLElement)#53 (1) {
      [0]=>
      string(15) "34.667201194399"
    }
    ["lon"]=>
    object(SimpleXMLElement)#52 (1) {
      [0]=>
      string(15) "135.49913552432"
    }
  }
  [2]=>
  array(3) {
    ["station"]=>
    string(15) "JR難波駅"
    ["lat"]=>
    object(SimpleXMLElement)#55 (1) {
      [0]=>
      string(14) "34.66667276344"
    }
    ["lon"]=>
    object(SimpleXMLElement)#54 (1) {
      [0]=>
      string(15) "135.49571895486"
    }
  }
}

 
完成です。
このようにSimpleXMLモジュールを使うと簡単に取得できます。
 

ただし、取得したデータが莫大な量になると、
アロー演算子で一つ一つ下るのはとても骨が折れる作業です。
解決策ではありませんが、get_object_varsを使うと、
一階層目までのオブジェクトをすべて、配列にすることができます。
 

$data = get_object_vars($object);
var_dump($data);

 
見てみます。
 

array(4) {
  ["Count"]=>
  string(1) "3"
  ["ViewCount"]=>
  string(1) "3"
  ["Query"]=>
  string(6) "難波"
  ["Item"]=>
  array(3) {
    [0]=>
    object(SimpleXMLElement)#49 (7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(9) "難波駅"
      ["Address"]=>
      string(36) "大阪府大阪市浪速区難波中"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#52 (2) {
        ["Lat"]=>
        string(11) "34.66005533"
        ["Lon"]=>
        string(12) "135.50483289"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#53 (2) {
        ["Lat"]=>
        string(15) "34.663312792164"
        ["Lon"]=>
        string(15) "135.50202380723"
      }
      ["Url"]=>
      object(SimpleXMLElement)#54 (0) {
      }
    }
    [1]=>
    object(SimpleXMLElement)#50 (7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(15) "近鉄難波駅"
      ["Address"]=>
      string(33) "大阪府大阪市中央区難波"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#55 (2) {
        ["Lat"]=>
        string(11) "34.66394422"
        ["Lon"]=>
        string(12) "135.50194444"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#56 (2) {
        ["Lat"]=>
        string(15) "34.667201194399"
        ["Lon"]=>
        string(15) "135.49913552432"
      }
      ["Url"]=>
      object(SimpleXMLElement)#57 (0) {
      }
    }
    [2]=>
    object(SimpleXMLElement)#51 (7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(15) "JR難波駅"
      ["Address"]=>
      string(33) "大阪府大阪市浪速区湊町"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#58 (2) {
        ["Lat"]=>
        string(11) "34.66341578"
        ["Lon"]=>
        string(12) "135.49852756"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#59 (2) {
        ["Lat"]=>
        string(14) "34.66667276344"
        ["Lon"]=>
        string(15) "135.49571895486"
      }
      ["Url"]=>
      object(SimpleXMLElement)#60 (0) {
      }
    }
  }

 
この関数では再帰的に全オブジェクトを配列にすることができないので、
ここから先の作業は、(array)で型キャストしてやるというのも一つの手です。
 

$data['Item'][0] = (array)$data['Item'][0];

 
一階層目に注目。
 

["Item"]=>
  array(3) {
    [0]=>
    array(7) {
      ["Category"]=>
      string(7) "Station"
      ["Title"]=>
      string(9) "難波駅"
      ["Address"]=>
      string(36) "大阪府大阪市浪速区難波中"
      ["AddressLevel"]=>
      string(1) "0"
      ["DatumTky97"]=>
      object(SimpleXMLElement)#52 (2) {
        ["Lat"]=>
        string(11) "34.66005533"
        ["Lon"]=>
        string(12) "135.50483289"
      }
      ["DatumWgs84"]=>
      object(SimpleXMLElement)#53 (2) {
        ["Lat"]=>
        string(15) "34.663312792164"
        ["Lon"]=>
        string(15) "135.50202380723"
      }
      ["Url"]=>
      object(SimpleXMLElement)#54 (0) {
      }

 
さらにまたもう一つ下の階層も型キャストしなければいけません。
これもめんどくさいですね。別の方法では、
SPL(Standard PHP Library)でIteratorインターフェイスを使い処理をするというのもあります。
ArrayObjectを使うとオブジェクトを配列のように扱うことができるようですが、
それはまた別の機会に。