ラベル convert の投稿を表示しています。 すべての投稿を表示
ラベル convert の投稿を表示しています。 すべての投稿を表示

2011年3月23日水曜日

PHPの SimpleXMLを配列に変換する

シンプルなXMLなら、SimpleXMLElementオブジェクトをarrayにキャストするだけで連想配列に変換できる。

$xml = '<?xml version="1.0" ?>
<root>
<test>TEST1</test>
<test>TEST2</test>
<attr trouble="MISSING">WHY?</attr>
</root>';

$sx = simplexml_load_string($xml);

var_dump((array)$sx);

array(2) {
  ["test"]=>
  array(2) {
    [0]=>
    string(5) "TEST1"
    [1]=>
    string(5) "TEST2"
  }
  ["attr"]=>
  string(4) "WHY?"
}

同じ要素名の要素がある場合(上記の例ではtest要素)、まとめて添字が0から始まる配列になる。
しかし、なぜか属性とテキストノードの両方がある要素の属性(上記の例ではattr要素のtrouble="MISSING")が無視される。

また、子要素や属性のあるXMLの場合、配列の中まで再帰的にキャストしていないのでSimpleXMLElementのままになってしまう。
$xml = '<?xml version="1.0" ?>
<root>
<parent>
<child>MUSUKO</child>
</parent>
<attr foo="FOO" bar="BAR" />
</root>';

$sx = simplexml_load_string($xml);

var_dump((array)$sx);

array(2) {
  ["parent"]=>
  object(SimpleXMLElement)#3 (1) {
    ["child"]=>
    string(6) "MUSUKO"
  }
  ["attr"]=>
  object(SimpleXMLElement)#2 (1) {
    ["@attributes"]=>
    array(2) {
      ["foo"]=>
      string(3) "FOO"
      ["bar"]=>
      string(3) "BAR"
    }
  }
}




これに対して、PHPマニュアルのコメント欄に一発で配列に変換する方法が書いてあった。
$xml = '<?xml version="1.0" ?>
<root>
<parent>
<child>MUSUKO</child>
</parent>
<attr foo="FOO" bar="BAR" />
</root>';

$sx = simplexml_load_string($xml);

var_dump(json_decode(json_encode($sx), true));

array(2) {
  ["parent"]=>
  array(1) {
    ["child"]=>
    string(6) "MUSUKO"
  }
  ["attr"]=>
  array(1) {
    ["@attributes"]=>
    array(2) {
      ["foo"]=>
      string(3) "FOO"
      ["bar"]=>
      string(3) "BAR"
    }
  }
}



json_decode()の第2引数(戻り値を連想配列にするかどうか)をtrueにするのがポイント。
属性は@attributesというKEYの連想配列に格納される。


しかし、属性とテキストノードの両方がある要素の属性が無視される問題は、このjson_encode/json_decodeを使った方法でも発症する。
$xml = '<?xml version="1.0" ?>
<root>
<attr trouble="MISSING">WHY?</attr>
</root>';

$sx = simplexml_load_string($xml);

var_dump(json_decode(json_encode($sx), true));

array(1) {
  ["attr"]=>
  string(4) "WHY?"
}

Why?
まあでも属性値がないことが分かっているXMLならこれで使えそう。(後から属性値を使いたいということになったら大変だけど。)

きちんとやりたいなら、SimpleXMLで取得したオブジェクトを属性も含めて配列に変換 | とりさんのソフト屋さんのやり方がスマートで良さそう。

2010年7月25日日曜日

PHPでの camelCaseへの変換と camelCaseからの変換のサンプル

アンダーバーやハイフンやスペースで単語を繋いだ文字列(例 foo_bar_baz)を、camelCaseやPascalCase(Upper Camel Case)に変換したいことがしばしばある。
その他にも先頭だけ大文字にしたかったり、各単語の先頭だけ大文字にしたかったりとか。
PHPにはucfirst()やucwords()があるから意外に楽にできる。



アンダーバー等で単語を繋いだ文字列を、他の形式に変換する

$separator = '_';    //区切り文字
$str = 'foo_bar_baz';  //変換対象の文字列

//先頭だけ大文字にする
echo ucFirst($str); // => Foo_bar_baz

//以降の変換の共通前処理
$ucSpace = ucWords(str_replace($separator, ' ', $str)); // => Foo Bar Baz

//各単語の先頭が大文字にする
echo str_replace(' ', $separator, $ucSpace); // => Foo_Bar_Baz

//PascalCase(先頭は大文字)にする
echo str_replace(' ', '', $ucSpace); // => FooBarBaz

//camelCase(先頭は小文字)にする ※要PHP5.3+
echo lcFirst(str_replace(' ', '', $ucSpace)); // => fooBarBaz
スペース区切りにすればucWords()が使えるのがミソ。もともと使いたい区切り文字がスペースならもっと楽。

lcfirst()のないPHP5.3未満でcamelCaseにするには少し面倒かも。
この方法が一番手軽そう(未検証)→ PHP: lcfirst - Manual



camelCaseから区切り文字で繋いだ形式に変換する
$separator = '_';    //区切り文字
$camel = 'fooBarBaz';
echo strToLower(preg_replace('/([a-z])([A-Z])/', "$1$separator$2", $camel)); // => foo_bar_baz
PascalCaseからTitle Case(スペース区切りで先頭だけ大文字の形式)への変換なら、strToLower()は要らない。



ハイフン区切りの文字列・先頭が大文字のスペース区切りの文字列・PascalCaseの文字列の相互変換ならこんな感じか。
function convertCase($str, $case) {
    switch ($case) {
        case 'hyphen':    // foo-bar-baz
            $str = preg_replace('/([a-z])([A-Z])/', "$1-$2", $str);
            return str_replace(' ', '-', strToLower($str));
        case 'normal':    // Foo bar baz
            $str = preg_replace('/([a-z])([A-Z])/', "$1 $2", $str);
            return str_replace('-', ' ', ucFirst(strToLower($str)));
        case 'pascal':    // FooBarBaz
            return str_replace(' ', '', ucWords(str_replace('-', ' ', $str)));
    }
}
正規表現だけでもいろいろできちゃいそうだけど、(正規表現を駆使出来ていない自分のような人には)見辛くなるのでstr_replace()を使った。(コードが横に長くなるし。)

ブログ アーカイブ

tags