PHPでXML文書をオブジェクトじゃなくて普通の配列としてパースしたり、普通の配列からXMLに変換する関数(成功編多分)
前回の記事でうまいこといったと思ったけど、
重複するタグが出てきた。
そういう訳で作り直した結果がこちら。
いろんな先人のコードを寄せ集めているだけなので、我ながらかなりずるい。
<?php /* vim: set fdm=marker: */ /* もともとのxml.phpは、内部的にutf-8で処理しているので、 ファイルの方で、毎回mb_convert_encodingしている。 従ってこっちのほうで、文字エンコードを変換しなくていい。 XML_unserialize( $buffer ); <- xmlからarray @see http://tk2-207-13211.vs.sakura.ne.jp/2015/08/278/ @see JAX http://0-oo.net/sbox/php-tool-box/jax @see https://qiita.com/suin/items/6d0032fe7ecc140395d8 */ //$var = Libs::parseAPI('', array('zn' => '1600000'), 'GET'); //$var = Libs::XMLtoArray("./5.xml"); //$fBuf = mb_convert_encoding($fBuf, "UTF-8", "sjis-win"); $xml = mb_convert_encoding(file_get_contents("./5.xml"), "UTF-8","sjis-win"); $var = XML_unserialize($xml); print_r($var); //print $var['diary']['article'][2]['contents']; //simpleXML用に文字列を作成 //$newxml = XML_serialize($var, ''); $newxml = XML_serialize($var, ""); print $newxml; $var = XML_unserialize($newxml); print_r($var); /** * 配列を再帰的にXML用の文字列に変換 * * foreachの$valueの値が配列でなくなるまで再帰 * ※ ['key' => array()]という場合も考慮 * @see http://tk2-207-13211.vs.sakura.ne.jp/2015/08/278/ * @param array or string $data 配列もしくは要素の中身 * @param string $name 要素の名前 例えば、これに"ABC"とすると、<abc>$dataをxmlにしたやつ</abc>になる。 * @return string $str */ function XML_serialize($data, $name = '') { /** {{{*/ return array2xml($data, $name); } /**}}}*/ /** * 配列(連想配列)をXMLにする * @param string $rootName root要素名 * @param array $arr * @return string */ function array2xml(array $arr,$rootName ) { /** {{{*/ $ret = ""; $ret = _toXmlString($arr, $rootName); $ret = str_replace( "<>", "", $ret ); $ret = str_replace( "</>", "", $ret ); //重複したxmlタグを一つにする. $pattern = '/(<.+>)(\1)/'; $replacement = '\1'; $ret = preg_replace ( $pattern, $replacement, $ret ); return cleanUpXML(_str2xml($ret)->asXML()); } /**}}}*/ /** * 再帰的に連想配列をXMLにする * @param string $name * @param mixed $data * @return string */ function _toXmlString($data, $name) { /** {{{*/ $s = ''; $attr = ''; if (is_array($data)) { foreach ($data as $k => $v) { if ($k === '@') { foreach ($v as $attrName => $attrValue) { $attr .= " $attrName=" . '"' . _xmlEscape($attrValue) . '"'; } } else if ($k === '#') { $s = _xmlEscape($v); } else if (is_numeric($k)) { $s .= _toXmlString($v, $name); } else { $s .= _toXmlString($v, $k); } } } else { $s = _xmlEscape($data); } return "<$name$attr>$s</$name>"; } /** }}}*/ /** * 文字列からSimpleXMLElementオブジェクトを生成する * @param string $xmlStr * @param string $nameSpace (Optional) XMLのNameSpace * @return SimpleXMLElement */ function _str2xml($xmlStr, $nameSpace = '') { /** {{{*/ return new SimpleXMLElement( $xmlStr, LIBXML_COMPACT | LIBXML_NOERROR, false, $nameSpace ); } /** }}}*/ /** * XMLの文字列エスケープ * @param string $value * @return string */ function _xmlEscape($value) { /** {{{*/ return htmlSpecialChars($value, ENT_QUOTES, 'UTF-8'); } /**}}}*/ /** XML_serializeから受けて呼ばれる. * 面倒だがXML_serialize_2は再帰関数なので、最後に<?xml version="1.0"?>を入れたいから仕方なく. * @param $data XML_serializeの$data * @param $name XML_serializeの$name * @ret <?xml version = "1.0" ?>なしのxmlデータ */ function XML_serialize_2($data, $name = '') { /** {{{*/ $str = ''; if(!empty($name)) { $str .= "<".$name.">"; } if(!is_array($data)) { $str .= $data; } else { foreach ($data as $key => $val) { if(is_numeric($key)) { /** 数字だったら */ $str .= XML_serialize_2($val, ''); } else { if(is_array($val) && !empty($val)) { $str .= XML_serialize_2($val, $key); } else { $str .= "<".$key.">"; $str .= (empty($val)) ? "" : $val; $str .= "</".$key.">"; } } } } if(!empty($name)) { $str .= "</".$name.">"; } return $str; } /**}}}*/ /** XML文書を解析して、配列として返す. * XML文書を、simplexml_load_string($xml)を利用して、配列に変換する. * その関数の構造上一番先頭のXMLタグは消去されてしまうため、 * 無理矢理dummyタグを入れ子にしている.これでダミータグが消えるだけですむので、 * ルートタグを含めて配列にできる. * @param $xml xml[必ずUTF-8] */ function XML_unserialize( $xml ) { /** {{{*/ //無理やり. //$xml = file_get_contents($fXmlPath); //一番先頭のタグの名前を取得する. //$rootTag = preg_match("正規表現のパタン", "入力文字列","検索結果"); preg_match("/<([a-z0-9]+)>/", $xml, $data_match); $rootTag = $data_match[1]; $xml = preg_replace("/<$rootTag>/", "<dummy><$rootTag>", $xml, 1);//1回だけ置換する. $xml = preg_replace("/<.$rootTag>/", "</$rootTag></dummy>", $xml, 1);//1回だけ置換する. // 帰ってきたXMLをパース $parse_xml = simplexml_load_string($xml); if (false === $parse_xml) return false; return xml2arr($parse_xml); } /**}}}*/ /** */ function xml2arr($xmlobj) { /** {{{*/ $arr = array(); if (is_object($xmlobj)) { $xmlobj = get_object_vars($xmlobj); } else { $xmlobj = $xmlobj; } foreach ($xmlobj as $key => $val) { if (is_object($xmlobj[$key])) { $arr[$key] = xml2arr($val); } else if (is_array($val)) { foreach($val as $k => $v) { if (is_object($v) || is_array($v)) { $arr[$key][$k] = xml2arr($v); } else { $arr[$key][$k] = $v; } } } else { $arr[$key] = $val; } } return $arr; } /**}}}*/ /** * SimpleXMLのasXML()をきれいにインデントする関数 * @package Sites * @author Suin <suinyeze@gmail.com> * @copyright 2011 Suin * @see https://qiita.com/suin/items/6d0032fe7ecc140395d8 * @license MIT License */ function cleanUpXML($string) { /** {{{*/ $string = preg_replace("/>\s*</", ">\n<", $string); $lines = explode("\n", $string); $string = ''; $indent = 0; foreach ( $lines as $line ) { $increment = false; $decrement = false; if ( preg_match('#<\?xml.+\?>#', $line) == true ) { // <?xml … } elseif ( preg_match('#<[^/].+>.*</.+>#', $line) == true ) { // Open Tag & Close Tag } elseif ( preg_match('#<.+/>#', $line) == true ) { // Self-closing Tag } elseif ( preg_match('#<!.*>#', $line) == true ) { // Comments and CDATA } elseif ( preg_match('#<[^/].+>#', $line) == true ) { // Open Tag $increment = true; } elseif ( preg_match('#</.+>#', $line) == true ) { // Close Tag $decrement = true; } else { // Others } if ( $decrement === true ) { $indent -= 1; } $string .= str_repeat("\t", $indent).$line."\n"; if ( $increment === true ) { $indent += 1; } } return $string; } /**}}}*/ ?>