ページ

2013/03/06

[PHP]簡易json2xml関数を無名関数を使って作ってみた

Twitter API が v1.1 から JSON のみを返すようになりましたね。
XML で保存していた過去のレスポンスと形式を揃えておくために
JSON から XML に変換したいと思ったら意外とめんどくさいですよん。
数行で出来るんじゃないのとか思って json2xml 関数を作ってみた。

php - How to convert array to SimpleXML - Stack Overflow
array_walk_recursive を使って
SimpleXMLElement::addChild で配列の中身を1個ずつ突っ込めばいいよ。
ただし key と value は反転するけどな!って回答があったので
無名関数で正しい順に渡してやればいいのでは?と書き始めた。
<?php
function json2xml($root, $json){
    $array = json_decode($json, true);
    $xml = new SimpleXMLElement("<{$root}/>");
    return array2xml($array, $xml);
}

function array2xml($array, &$xml){
    $addChild = function($value, $key) use($xml){
        if(is_array($value)){
            if(is_numeric($key)){
                array2xml($value, $xml);
            }else{
                array2xml($value, $xml->addChild($key));
            }
        }else{
            $xml->addChild($key, $value);
        }
    };
    array_walk($array, $addChild);
    return $xml->asXML();
}
そしたら添字配列と連想配列で微妙に処理を変えないとタグ名が意図した通りにならなくて
php - How to convert array to SimpleXML - Stack Overflow を見ると
再起処理で変換していかなくてはならなくて、 array2xml なんかも作ってしまったり。
実行結果は以下のような感じ。
<?php
$json = '{"hashtags":[{"indices":[32,36],"text":"lol"}]}'; // https://dev.twitter.com/docs/platform-objects/entities
$xml = json2xml('response', $json); 
/*
<?xml version="1.0"?>
<response><hashtags><indices><0>32</0><1>36</1></indices><text>lol</text></hashtags></response>
*/
添字配列のところが数値タグになっていて XML 的にアレ。
以下の xml2json で再変換したらエラーで死ぬ。
<?php
function xml2json($xmlString){
    $xml = simplexml_load_string($xmlString);
    $json = json_decode(json_encode($xml), true);
    return $json;
}
やってみると意外と考えなきゃいけないことが多いというわけ。
自分で変換とか茨の道過ぎてオススメできない。
先人がPHPでJSONとArrayとXMLを一発で相互変換するツール - JAX.php [ゼロと無限の間に]
作成しているのでありがたく使わせていただこう。
しかし、少しだけバグがあって JSON に XML でエスケープすべき文字が含まれている場合、
エスケープしていないことによるエラーが出る。
_toXmlString 関数で $s = $data; の代入している際にデータをエスケープしていないので
_xmlEscape でエスケープした $s = $this->_xmlEscape($data); に書き換えてから使おう。
この便利な JAXクラスで json2xml すると上でちゃんと変換できなかった JSON も
以下のように正しい XML が返ってくる。
<?xml version="1.0"?>
<response><hashtags><hashtags><indices><indices>32</indices><indices>36</indices></indices><text>lol</text></hashtags></hashtags></response>
先人様様である。感謝して使おう。