PHP: XML to Array and backwards
Here the XML with PHP solution: XML->Array and Array->XML.
Work with it as with usual array.
Format XML->Array
_c - children
_v - value
_a - attributes
This is 1.1 :)
Example #1 (1.xml):
<ddd>
<onemore dd="55">
<tt>333</tt>
<tt ss="s1">555</tt>
<tt>777</tt>
</onemore>
<two>sdf rr</two>
</ddd>
The code:
$xml=xml2ary(file_get_contents('1.xml'));
print_r($xml);
Here is the Array result:
Array
(
[ddd] => Array (
[_c] => Array (
[onemore] => Array (
[_a] => Array (
[dd] => 55
)
[_c] => Array (
[tt] => Array (
[0] => Array (
[_v] => 333
)
[1] => Array (
[_a] => Array (
[ss] => s1
)
[_v] => 555
)
[2] => Array (
[_v] => 777
)
)
)
)
[two] => Array (
[_v] => sdf rr
)
)
)
)
Example #2: (1.xml as described before)
$xml=xml2ary(file_get_contents('1.xml'));
$xml['ddd']['_c']['twomore']=$xml['ddd']['_c']['onemore'];
$xml['ddd']['_c']['twomore']['_c']['tt'][0]['_v']='hello';
echo ary2xml($xml);
Will output result:
<ddd>
<onemore dd="55">
<tt>hello</tt>
<tt ss="s1">555</tt>
<tt>777</tt>
</onemore>
<two>sdf rr</two>
<twomore dd="55">
<tt>hello</tt>
<tt ss="s1">555</tt>
<tt>777</tt>
</twomore>
</ddd>
THE Sources: :)
/*
Working with XML. Usage:
$xml=xml2ary(file_get_contents('1.xml'));
$link=&$xml['ddd']['_c'];
$link['twomore']=$link['onemore'];
// ins2ary(); // dot not insert a link, and arrays with links inside!
echo ary2xml($xml);
*/
// XML to Array
function xml2ary(&$string) {
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parse_into_struct($parser, $string, $vals, $index);
xml_parser_free($parser);
$mnary=array();
$ary=&$mnary;
foreach ($vals as $r) {
$t=$r['tag'];
if ($r['type']=='open') {
if (isset($ary[$t])) {
if (isset($ary[$t][0])) $ary[$t][]=array(); else $ary[$t]=array($ary[$t], array());
$cv=&$ary[$t][count($ary[$t])-1];
} else $cv=&$ary[$t];
if (isset($r['attributes'])) {foreach ($r['attributes'] as $k=>$v) $cv['_a'][$k]=$v;}
$cv['_c']=array();
$cv['_c']['_p']=&$ary;
$ary=&$cv['_c'];
} elseif ($r['type']=='complete') {
if (isset($ary[$t])) { // same as open
if (isset($ary[$t][0])) $ary[$t][]=array(); else $ary[$t]=array($ary[$t], array());
$cv=&$ary[$t][count($ary[$t])-1];
} else $cv=&$ary[$t];
if (isset($r['attributes'])) {foreach ($r['attributes'] as $k=>$v) $cv['_a'][$k]=$v;}
$cv['_v']=(isset($r['value']) ? $r['value'] : '');
} elseif ($r['type']=='close') {
$ary=&$ary['_p'];
}
}
_del_p($mnary);
return $mnary;
}
// _Internal: Remove recursion in result array
function _del_p(&$ary) {
foreach ($ary as $k=>$v) {
if ($k==='_p') unset($ary[$k]);
elseif (is_array($ary[$k])) _del_p($ary[$k]);
}
}
// Array to XML
function ary2xml($cary, $d=0, $forcetag='') {
$res=array();
foreach ($cary as $tag=>$r) {
if (isset($r[0])) {
$res[]=ary2xml($r, $d, $tag);
} else {
if ($forcetag) $tag=$forcetag;
$sp=str_repeat("\t", $d);
$res[]="$sp<$tag";
if (isset($r['_a'])) {foreach ($r['_a'] as $at=>$av) $res[]=" $at=\"$av\"";}
$res[]=">".((isset($r['_c'])) ? "\n" : '');
if (isset($r['_c'])) $res[]=ary2xml($r['_c'], $d+1);
elseif (isset($r['_v'])) $res[]=$r['_v'];
$res[]=(isset($r['_c']) ? $sp : '')."</$tag>\n";
}
}
return implode('', $res);
}
// Insert element into array
function ins2ary(&$ary, $element, $pos) {
$ar1=array_slice($ary, 0, $pos); $ar1[]=$element;
$ary=array_merge($ar1, array_slice($ary, $pos));
}
31 comments:
Thanks!
You solved my problem
Just what I needed! :)
Perhaps you should put a if(!function_exists()).. around the internal declaration of _del_p. If you use xml2ary more than once, PHP complains.
To anonymous:
Thanks for your idea about _del_p. Bug is fixed now:)
nice job!
Hi,
great function!
I'm trying to use it to read from an external xml file. The xml file is actually an url (google maps). Somehow I can't get it working.
I first declare the url this way:
$xml_location = "'http://maps.google.com/maps/geo?q=$location&output=xml&key=ABQIAAAAJfKsQWO6RF10dFkeiMCQ-RTuc1leHq4ZyxUzjHJLRPnNfWTYLhSmhVnunLB95kDrffV4E0R-m4xnUw'";
Then I place that variable into the
function this way:
$xml=xml2ary(file_get_contents($xml_location));
I get the following error:
Warning: file_get_contents('http://maps.google.com/maps/geo?q=nederland&output=xml&key=ABQIAAAAJfKsQWO6RF10dFkeiMCQ-RTuc1leHq4ZyxUzjHJLRPnNfWTYLhSmhVnunLB95kDrffV4E0R-m4xnUw') [function.file-get-contents]: failed to open stream: No such file or directory in /home/sites/site19/web/maps/map.php on line 158
When I put the contents of $xml_location directly into the function. For example:
$xml=xml2ary(file_get_contents('http://maps.google.com/maps/geo?q=nederland&output=xml&key=ABQIAAAAJfKsQWO6RF10dFkeiMCQ-RTuc1leHq4ZyxUzjHJLRPnNfWTYLhSmhVnunLB95kDrffV4E0R-m4xnUw'));
It does work...
What's going wrong over here?
Great piece of code! Thanks!
Wouldn't be a good idea adding support for html content on ary2xml function? I made a small mod for your code, adding the CDATA tag between the value (couldn't paste the code)
just my 2 cents, thanks ;)
To Tomek: You got an error "No such file or directory" becouse of $xml_location = "'http..'" (superfluous single quotes)
To Mauricio: please send me by email your code about CDATA, and I'll add it into this post.
Hi all!
the Mauricio's CDATA hack is there: http://forums.kayako.com/showthread.php?t=11269
Search "// small hack ;)" over the page; above this text is the hack.
Bye!!
You're a lifesaver. Quite a brilliant little script. EXACTLY what I needed.
GREAT! Thanks! ;-)
Hola, bro!
A nice piece of work.
1. ary2xml() returns xml without end tag. Is it a feature?
2. May be it is better to have it with a header which states that is xml script&
Of course, I made these changes and using it. If anybody needs this function with above features included, just let me know at sapargali at gmail dot com.
Thank you ! This is a nice script that solved a part of my problem.
Thanks a lot for the BRILLIANT script!!! :D
You save me from hours of headaches! ^__^
Thanks for saving me hours of work!
awesome script, thanks! Nice logical structure and saved me a lot of time :)
greate script... but how do i escape or other html tags from beeing arrayed? :D
Excellent script ... thanks for saving many hours of work
very cool script, thanks - learning this stuff and this is very useful.
PERFECT!!! I have been looking around FOREVER for a nice xml=>array class/functions out there. I modified your program to be class-based, but other than that, this is SAAAAWEEEEET!!! I love the fact that it standardizes everything...like if you have a subarray, it automatically sets it up to have a 0 index, rather than waiting until there are multiple children before making it. That is easily the sweetest thing that I have ever seen!!!!!!
Thanks a lot
Plug&play ... really!! Thks.
I changed:
elseif (isset($r['_v'])) $res[]=$r['_v']
to:
elseif (isset($r['_v'])) $res[]=htmlspecialchars($r['_v'])
.
Now values like "hal&lo" will go well.
Excelent, thanks a lot.
Hay to wigle with it a little, but it served it's purpose.
Only script around that did this thing.
Of course when you have acces to PHP5 there's simpleXML.
Hey.. this is a super tool. Quick question for you. I would like all attributes to have a numerical index. Is this possible? Example:
[ddd]
[onemore dd="55"]
[tt]333[/tt]
[tt ss="s1"]555[/tt]
[tt]777[/tt]
[/onemore]
[two]sdf rr[/two]
[/ddd]
(switched < to [, to post)
becomes this:
Array
(
[ddd] => Array (
[0] => Array (
[_c] => Array (
[onemore] => Array (
[0] => Array (
[_a] => Array (
[dd] => 55
)
[_c] => Array (
[tt] => Array (
[0] => Array (
[_v] => 333
)
...
any ideas?
This way I can use a uniform parse on all depths of the array, instead of checking if key [0] exists, and so on... THanks
Brilliant! Worked flawlessly! Can I buy you a Guiness?
Wikkked, I spent 1 week looking for xml -> array and array -> xml... checked out SPL, simpleXML but just came across this script and it works sweeet as... Q: How do you save the xml? e.g. saveXML()?? Thanks, James from NZ.
Very awesome!
If you have time, could you show an example of someone selecting the values of the tt element where the ss attribute equals s1?
This doesn't seem to work:
$xml['ddd']['_c']['onemore']['_c']['tt']['_a']['ss']['s1']['_v']
@Todd
try: $xml['ddd']['_c']['onemore']['_c']['tt']['_a']['ss']['_v']['s1']
_c, _v, _a looks like it comes before the result
Great! thanks a lot as I'v been hitting my head against the wall for a good week now.
At last a simple function to extract xml into a array and hich support multiples tags with the same name and supports attributes as well.
Really saved my day
I'm confused how to actually traverse this multidimensional array.
if I have:
$xml = xml2ary(file_get_contents($url));
then my xml gets stored as an array successfully.
But how do I traverse it? I tried a for loop, but it's not working. I don't know the values that are in the array and all the examples you have assume that you know the keys. Can you help me with this?
Post a Comment