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

PHPのsimplexml_load_file()でXMLを扱うときに注意すること

はじめに

XMLは、JSONと違って同じ名前の要素を複数持つことができるのですが、PHPのsimplexml_load_file関数で扱うときにちょっとめんどくさかったのでメモ。

もっといい解決策があるかもしれません。

実験

入力XML

list1.xml

<?xml version="1.0" encoding="utf-8"?>
<data>
  <book>
      <title>Swiftではじめる iPhoneアプリ開発の教科書</title>
      <caption>Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。</caption>
      <date>2015/10/30</date>
  </book>
</data>

list2.xml

<?xml version="1.0" encoding="utf-8"?>
<data>
  <book>
      <title>詳細! Swift 2 iPhoneアプリ開発 入門ノート</title>
      <caption>待ったなし! Swift 2</caption>
      <date>2015/11/14</date>
  </book>
  <book>
      <title>Swiftではじめる iPhoneアプリ開発の教科書</title>
      <caption>Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。</caption>
      <date>2015/10/30</date>
  </book>
</data>

list1.xmlはbookタグが1個、list2.xmlはbookタグが2個存在します。

コード1

<?php
$xml1 = simplexml_load_file('./list1.xml');
$xml2 = simplexml_load_file('./list2.xml');

$xml_array1 = json_decode(json_encode($xml1), true);
$xml_array2 = json_decode(json_encode($xml2), true);

print_r($xml_array1);
print_r($xml_array2);

SimpleXMLElementオブジェクトを配列に変換する方法は、公式ドキュメントのコメントを参考に。

json_decodeの第2引数をtrueにすることで連想配列にしています。

結果

temp $ php xml2array1.php
Array
(
  [book] => Array //連想配列
      (
          [title] => Swiftではじめる iPhoneアプリ開発の教科書
          [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
          [date] => 2015/10/30
      )

)
Array
(
  [book] => Array //連想配列の配列
      (
          [0] => Array
              (
                  [title] => 詳細! Swift 2 iPhoneアプリ開発 入門ノート
                  [caption] => 待ったなし! Swift 2
                  [date] => 2015/11/14
              )

          [1] => Array
              (
                  [title] => Swiftではじめる iPhoneアプリ開発の教科書
                  [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
                  [date] => 2015/10/30
              )

      )

)
temp $

book要素が1個の場合と2個の場合で結果が異なります。 これでは、viewに表示するときなどに困りますね。

コード2

<?php
$xml1 = simplexml_load_file('./list1.xml');

$xml_array1 = json_decode(json_encode($xml1), true);
print_r($xml_array1);

foreach($xml_array1 as $key => $value) {
  // 連想配列だったら配列に変換
  if (is_array($value) && array_values($value) !== $value) {
  if (array_values($value) !== $value) {
    $xml_array1 = array($key => array($value));
  }
}
print_r($xml_array1);

結果

temp $ php xml2array2.php
Array
(
  [book] => Array
      (
          [title] => Swiftではじめる iPhoneアプリ開発の教科書
          [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
          [date] => 2015/10/30
      )

)
Array
(
  [book] => Array //要素1の連想配列の配列
      (
          [0] => Array
              (
                  [title] => Swiftではじめる iPhoneアプリ開発の教科書
                  [caption] => Appleのプログラミング言語、「Swift」バージョン2対応の、iPhoneアプリ作成入門書です。
                  [date] => 2015/10/30
              )

      )

)
temp $

これで同じ構造になりました。

おまけ

if (array_values($value) !== $value)は何をしているのか。

公式ドキュメントからこの関数の説明を引用します。

array_values() は、配列 array から全ての値を取り出し、数値添字をつけた配列を返します。

つまり、配列の場合はarray_valuesを使っても何も変わりませんが、連想配列であれは値だけになると!==となるので、連想配列かどうかを判定できます。

参考

SimpleXML 関数

array_values