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));
}
60 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?
Wow, Great Work. This helped me tremendously at the last minutes. Thanks for your great work.
Google checkout is using a modified version of this in their PHP code.
mapping is interesting but it's better to omit the _v and put the value (if any) in _c. because an element can't have both value and children at the same time. when using the value code must check if the _c is a value or an array!!!
Thanks a million for this. Been struggling for a while until I found your solution - solved the problem in an afternoon.
Great job in those functions they are just what i needed. Thanks!
hi gentlepeople,
don't want to spoil the party here, but this somewhat larger xml isn't parsed beyond the 1st child:
here it is:
http://www.socon.nl/bigfile.xml
has anybody advise on this?
Harry
Great Job !!!!!!!!
Great Coder !!!!!!!
Great Mind :)
Thank you very much for giving this for free !!
Hi,
Good Work.
When i am trying
ary2xml()it not returns xml code..
i do print_r(ary2xml($xml)); just getting same array again..
I want same xml again & save it ti same pllace..
Thanks in advance
Wonderful!
This function rocks. Been using this function perfectly, but in my last project I'm using it with an exported XML file (multitabs) from Excel and it gives me a weird error or well no error, it just stops working :/
The exact problem is that when parsing it shows what should be a value( _v) as a child (_c) which then messes up the entire array of course and at that time the function ends all arrays and thus stops parsing. Seeing as this is midway the first tab, I'm a little confused, there's nothing wrong with the text in that value and it has parsed most of the others before it perfectly.
I'm a little confused why it would do that, it has worked perfectly in the past. I double checked the xml file, but that's all solid. Any known bug that might cause this??
While this works reasonably well in procedural scripts, it's useless for OOP apps. How about upgrading and presenting us with an awesome state-of-the-art OOP-class?
keep up the good work
mr.spots
Nice Script. But not able to parse big XML? Why so?
This was so useful, thanks very much.
2 years still relevant and useful. Thx.
Anup, parsing big XML file may require you to change your php.ini resource limits settings, to allow php to allocate more memory, I've set it to 128M and it worked.
BTW, great script, I finally got what I was looking for ages to do, so simple and effective!!!
Thanks a bunch!
Fantastic - have tried a number of different XML->Array converters and this is the only one that's worked perfectly. Thanks!
Great code, I've been hunting around for ages for code to do this. Perfect, absolutely perfect, many thanks
simply amazing! thanks so much :)
Really great job.
thanks a lot, very effective and easy to understandable script is this.
This one i will say the best script i have seen ever.
WOW this is a fantastic script! I have spent HOURS trying to work this info and here it is. I wish I had tried this first. THANK YOU SO MUCH!
Just wanted to say, I was searching for XML to Array scripts and having a horrible time - they all had some fatal flaw (failed to deal with attributes, required installing binary extensions to PHP that are not commonly available on shared hosts, etc.).
This is the only one that worked straight out of the box and that extracts all of the useful information from the XML file. Great job, and thanks for contributing!
Many thanks for this. Works very well. Though there's one anomaly which took me a while to work out. If a branch of the XML has multiple entries, the code creates an array, but otherwise it only creates a single variable. So my code for retrieving something from $xml needed to be slightly different, without an array subscript, in this case. Might be useful if it created arrays every time, even with just a single element.
Possibly the most interesting thing I have read all week...
http://lobinsurance.info
thanks for this tips
Thanks for the function. I modified it a little so that it parses an XML file instead of a string.
You can do this by changing the argument to ask for a URL, then add the following lines right after the {:
$handle = fopen($file, "r");
$string = fread($handle);
Great! It works
Very nice function. Many thanks.
Thanks for the script.
Only find one bug. If there is a & sign in a tag xml & array script will stop working anybody a tweak/bugfix for this.
Post a Comment