Most of the time we need to convert the XML to array or JSON , but now I have to completed the requirement of converting XML to XPath which make our template easily geting the data from XML data source due to the Xpath mapping .Here is the function:
<?php
function sxiToXpath($sxi, $key = null, &$tmp = null)
{
$keys_arr = array();
//get the keys count array
for ($sxi->rewind(); $sxi->valid(); $sxi->next())
{
$sk = $sxi->key();
if (array_key_exists($sk, $keys_arr))
{
$keys_arr[$sk]+=1;
$keys_arr[$sk] = $keys_arr[$sk];
}
else
{
$keys_arr[$sk] = 1;
}
}
//create the xpath
for ($sxi->rewind(); $sxi->valid(); $sxi->next())
{
$sk = $sxi->key();
if (!isset($$sk))
{
$$sk = 1;
}
if ($keys_arr[$sk] >= 1)
{
$spk = $sk . '[' . $$sk . ']';
$keys_arr[$sk] = $keys_arr[$sk] - 1;
$$sk++;
}
else
{
$spk = $sk;
}
$kp = $key ? $key . '/' . $spk : '/' . $sxi->getName() . '/' . $spk;
if ($sxi->hasChildren())
{
sxiToXpath($sxi->getChildren(), $kp, $tmp);
}
else
{
$tmp[$kp] = strval($sxi->current());
}
$at = $sxi->current()->attributes();
if ($at)
{
$tmp_kp = $kp;
foreach ($at as $k => $v)
{
$kp .= '/@' . $k;
$tmp[$kp] = $v;
$kp = $tmp_kp;
}
}
}
return $tmp;
}
function xmlToXpath($xml)
{
$sxi = new SimpleXmlIterator($xml);
return sxiToXpath($sxi);
}
/**
* How to use the function
*/
$xml = <<<EOT
<?xml version="1.0" encoding="utf8" ?>
<data>
<item ID="30001">
<Company>Navarro Corp.</Company>
</item>
<item ID="30002" IDd="30002">
<Company>Performant Systems</Company>
</item>
<item ID="30003">
<Company id='id_test'><g id='id_g'>glove</g></Company>
</item>
<item>
</item>
</data>
EOT;
$rs = xmlToXpath($xml);
print_r($rs);
/**
* the results may lkie this:
Array
(
[/data/item[1]/Company[1]] => Navarro Corp.
[/data/item[1]/@ID] => SimpleXMLIterator Object
(
[0] => 30001
)
[/data/item[2]/Company[1]] => Performant Systems
[/data/item[2]/@ID] => SimpleXMLIterator Object
(
[0] => 30002
)
[/data/item[2]/@IDd] => SimpleXMLIterator Object
(
[0] => 30002
)
[/data/item[3]/Company[1]/g[1]] => glove
[/data/item[3]/Company[1]/g[1]/@id] => SimpleXMLIterator Object
(
[0] => id_g
)
[/data/item[3]/Company[1]/@id] => SimpleXMLIterator Object
(
[0] => id_test
)
[/data/item[3]/@ID] => SimpleXMLIterator Object
(
[0] => 30003
)
[/data/item[4]] =>
)
*/
echo "Total:" . count($rs);
echo "<hr>";
/* You can check the xpath result like this */
$xml = new SimpleXMLElement($xml);
foreach ($rs as $k => $v)
{
echo "Xpath:" . $k . " |Value:" . $v . " ";
var_dump($xml->xpath($k));
echo "<br>";
}
The SimpleXMLIterator class
(PHP 5 >= 5.1.0)
Introduction
The SimpleXMLIterator provides recursive iteration over all nodes of a SimpleXMLElement object.
Class synopsis
SimpleXMLIterator
extends
SimpleXMLElement
implements
RecursiveIterator
,
Traversable
,
Iterator
,
Countable
{
/* Methods */
/* Inherited methods */
final public SimpleXMLElement::__construct
( string
$data
[, int $options = 0
[, bool $data_is_url = false
[, string $ns = ""
[, bool $is_prefix = false
]]]] )
public void SimpleXMLElement::addAttribute
( string
$name
[, string $value
[, string $namespace
]] )
public SimpleXMLElement SimpleXMLElement::addChild
( string
$name
[, string $value
[, string $namespace
]] )
public SimpleXMLElement SimpleXMLElement::attributes
([ string
}$ns = NULL
[, bool $is_prefix = false
]] )Table of Contents
- SimpleXMLIterator::current — Returns the current element
- SimpleXMLIterator::getChildren — Returns the sub-elements of the current element
- SimpleXMLIterator::hasChildren — Checks whether the current element has sub elements.
- SimpleXMLIterator::key — Return current key
- SimpleXMLIterator::next — Move to next element
- SimpleXMLIterator::rewind — Rewind to the first element
- SimpleXMLIterator::valid — Check whether the current element is valid
hezll at msn dot com ¶
21 days ago
ratfactor at gmail dot com ¶
3 years ago
The documentation is a bit sparse for SimpleXmlIterator. Here is an example showing the use of its methods. xml2Array and sxiToArray work together to convert an XML document to an associative array structure.
The contents of cats.xml:
======================================
<cats>
<cat>
<name>Jack</name>
<age>2</age>
<color>grey</color>
<color>white</color>
</cat>
<cat>
<name>Maxwell</name>
<age>12</age>
<color>orange</color>
<color>black</color>
</cat>
</cats>
======================================
<?php
function xml2array($fname){
$sxi = new SimpleXmlIterator($fname, null, true);
return sxiToArray($sxi);
}
function sxiToArray($sxi){
$a = array();
for( $sxi->rewind(); $sxi->valid(); $sxi->next() ) {
if(!array_key_exists($sxi->key(), $a)){
$a[$sxi->key()] = array();
}
if($sxi->hasChildren()){
$a[$sxi->key()][] = sxiToArray($sxi->current());
}
else{
$a[$sxi->key()][] = strval($sxi->current());
}
}
return $a;
}
// Read cats.xml and print the results:
$catArray = xml2array('cats.xml');
print_r($catArray);
?>
Results (reformatted a bit for compactness and clarity):
======================================
Array(
[cat] => Array(
[0] => Array(
[name] => Array( [0] => Jack )
[age] => Array( [0] => 2 )
[color] => Array( [0] => grey,
[1] => white )
)
[1] => Array(
[name] => Array( [0] => Maxwell )
[age] => Array( [0] => 12 )
[color] => Array( [0] => orange
[1] => black )
)
)
)
ajnsit dot NOSPAM at gmail dot com ¶
2 years ago
Here's a simple function to convert an XML string to an array -
<?php
// PHP5.3 and above only
function parse($str) {
$f = function($iter) {
foreach($iter as $key=>$val)
$arr[$key][] = ($iter->hasChildren())?
call_user_func (__FUNCTION__, $val)
: strval($val);
return $arr;
};
return $f(new SimpleXmlIterator($str, null));
}
?>
PHP 5.2 and below do not have anonymous functions.
But you can create a helper function to achieve the same thing -
<?php
function parse($str) {
return parseHelper(new SimpleXmlIterator($str, null));
}
function parseHelper($iter) {
foreach($iter as $key=>$val)
$arr[$key][] = ($iter->hasChildren())?
call_user_func (__FUNCTION__, $val)
: strval($val);
return $arr;
}
?>
Using it is straightforward enough -
<?php
$xml = '
<movies>
<movie>abcd</movie>
<movie>efgh</movie>
<movie>hijk</movie>
</movies>';
var_dump(parse($xml));
?>
This will output -
array
'movie' =>
array
0 => string 'abcd' (length=4)
1 => string 'efgh' (length=4)
2 => string 'hijk' (length=4)
David Lanstein ¶
4 years ago
Unlike the DirectoryIterator, for one, you'll need to call $it->rewind() before using the iterator in a for() or while() loop. Calling foreach() does rewind the iterator before iteration.
