[PHP] xmlを配列にする。

PHPで、XML形式のテキストデータを、連想配列にする関数を作りました。

PHPでXMLを配列にするものは既に存在します。

PHPで、XML形式のテキストデータを、連想配列にするというと、似たようなものとしてこういうのがありますね。

はい。そういうわけで、実は既にあるんです。
既にあるのに、同じようなものを作る。
こういう行為を「車輪の再発明」なんて言ったりします。

PHPでXMLを配列にするものは既に存在するけど・・・

とはいえ、今回作ったのには、やはり理由がありまして、
その、
なんていうか、メンドクサイじゃないですか?

↑ であげた既存のものを使っていらっしゃるかたはいるでしょうか?
ご存知のない方は、Googleで軽く調べて頂ければと思うのですが、なんだかややこしくないですか?

XMLを連想配列にしたいだけなのに。

で、PHPでXMLを配列にする関数を作りました

これが、今回作った関数になります。

/*
 * simplexml形式を連想配列にする。
 * $sxml = simplexml_load_string($xml);
 * $retArray = xml2array($sxml);
 */
function xml2array(&$sxml, $isRoot = true){
  if ($isRoot)
    return array($sxml->getName() => array(xml2array($sxml, false)));
  $r = array();
  foreach($sxml->children() as $cld){
    $a = &$r[(string)$cld->getName()];
    $a = &$a[count($a)];
    if (count($cld->children()) == 0)
      $a['_value'] = (string)$cld;
    else
      $a = xml2array($cld, false);
    foreach($cld->attributes() as $at)
      $a['_attr'][(string)$at->getName()] = (string)$at;
  }
  return $r;
}

これだけです。
16行足らずですが、これだけです。

その代わりと言ってはなんですが、なんでもかんでも対応しているわけではありません。
[CDATA] など、凝ったものは省いております。
それと、simpel_xmlを扱いますので、PHP5以上の対応となります。

PHPでXMLを配列にするxml2array()で対応していること

まず、XMLのツリー形式そのままで、多階層な連想配列にします。
値(value)と属性値(attribute)を取り込みます。

PHPでXMLを配列にするxml2array()の使い方例

サンプルとして実際のコードを書いてみます。

//  XMLを用意します。
//  ファイルから読みこんでもOKです。
$xml =<<<EOT
<?xml version="1.0" encoding="utf-8" ?>
<area>
  <pref code="0001">
    <name no="1">北海道</name>
    <kana>ほっかいどう</kana>
  </pref>
  <pref code="0002">
    <name no="2">青森県</name>
    <kana>あおもりけん</kana>
  </pref>
  <pref code="0003">
    <name no="3">岩手県</name>
    <kana>いわてけん</kana>
  </pref>
</area>
EOT;

//  simplexml として、読みこみます。
$sxml = simplexml_load_string($xml);

//  xml2array に渡します。
$retArray = xml2array($sxml);

ここまでで、$xmlは、$retArray として連想配列になっています。

試しに出力してみましょう。

//  for文で、取り出してみます。
foreach($retArray['area'] as $area){
  foreach($area['pref'] as $pref){
    $name = $pref['name'][0];
    $kana = $pref['kana'][0];
    echo $pref['_attr']['code'];
    echo ' : ' . $name['_value'];
    echo '(' . $name['_attr']['no'] . ')';
    echo ' : ' . $kana['_value'];
    echo '<br />';
  }
}

↓下記のように出力されます。

0001 : 北海道(1) : ほっかいどう
0002 : 青森県(2) : あおもりけん
0003 : 岩手県(3) : いわてけん

print_rで、配列の中を覗いてみます。

//  print_r で、配列の中を出力します。
echo '<pre>';
print_r($retArray);
echo '</pre>';

↓ 下記が出力されます。

Array
(
  [area] => Array
    (
      [0] => Array
        (
          [pref] => Array
            (
              [0] => Array
                (
                  [name] => Array
                    (
                      [0] => Array
                        (
                          [_value] => 北海道
                          [_attr] => Array
                            (
                              [no] => 1
                            )
                        )
                    )
                  [kana] => Array
                    (
                      [0] => Array
                        (
                          [_value] => ほっかいどう
                        )
                    )
                  [_attr] => Array
                    (
                       => 0001
                    )
                )
              [1] => Array
                (
                  [name] => Array
                    (
                      [0] => Array
                        (
                          [_value] => 青森県
                          [_attr] => Array
                            (
                              [no] => 2
                            )
                        )
                    )
                  [kana] => Array
                    (
                      [0] => Array
                        (
                          [_value] => あおもりけん
                        )
                    )
                  [_attr] => Array
                    (
                       => 0002
                    )
                )
              [2] => Array
                (
                  [name] => Array
                    (
                      [0] => Array
                        (
                          [_value] => 岩手県
                          [_attr] => Array
                            (
                              [no] => 3
                            )
                        )
                    )
                  [kana] => Array
                    (
                      [0] => Array
                        (
                          [_value] => いわてけん
                        )
                    )
                  [_attr] => Array
                    (
                       => 0003
                    )
                )
            )
        )
    )
)

PHPでXMLを配列にするポイント

print_rの結果をご覧頂いたように、XMLのツリー構造を保ったまま連想配列にしています。
ここでいくつかポイントを書いておきます。

ノードの扱いについて

ノードは、自身の直下に、配列を作ります。
ノードでは、 pref[0], pref[1], ppref[2] と3つ作られています。
pref[0]が、XMLで最初に現れるに対応します。
pref[1]は2番目に、pref[2]は3番目のに対応・・・と続きます。

末端ノードのとき

ノードが末端ノードの時は、その値を['_value'] に、値がセットされます。
※ _value とアンダーバーから始まることに注意してください。

属性の参照

属性値(Attribute) は、 ['_attr'] にセットされます。
['_attr'] の中の、添え字は、XMLで使われている属性名となります。
※ _attr とアンダーバーから始まることに注意してください。

XMLに名前空間が使われていたら・・・

simple_xmlで、名前空間を扱おうとすると少々面倒な記述になります。
具体的には、 $simple_xml->children('名前空間のURI'); といった感じで、単純にchildrenだけでは取れません。
ちなみに、XMLで定義されている名前空間は、$simple_xml->getDocNamespaces(true); これで取ることができます。

ただ・・・、実際の場面において、名前空間まで意識することはあまり無いかと思います。
少なくとも、私が扱うなかでは、名前空間まで厳密にチェックして値を取得する、ほどのことは必要とすることがまずありません。

そこで、元のXMLから、名前空間の指定を単純に取り外すことで、名前空間付きのXMLに対応することにします。

$xml = preg_replace('/<([/]?)[A-Za-z0-9]+:([A-Za-z0-9]+)( xmlns:[^>]+)?>/', '<12>', $xml);
$sxml = simplexml_load_string($xml);

このように、simplexml_load する前に、preg_replace で、名前空間の指定を取り外します。

※ 名前空間まで厳密にチェックする必要があるかどうかは、案件ごとに異なると思います。
ご使用の際は、案件の仕様を満たすかどうか、ご自身の判断でお使いください。

広告