Hi I am trying to use XML Reader to read some dynamic xml where I don't know the element names. I need to use XML Reader and not SimpleXML as that is all that is available on my server. The trouble I am having is trying to have something that can deal with both nested and un-nested elements. My XML looks like
<status>
<state>active</state>
<time>2014-07-15T04:00:00.000Z</time>
<output-type>archive</output-type>
<sources>
<source>
<class>audio</class>
<channels>
<channel>
<position>left</position>
<average>0</average>
<peak>0</peak>
</channel>
<channel>
<position>right</position>
<average>1095</average>
<peak>1114</peak>
</channel>
</channels>
</source>
</status>
But Could also look like:
<status>inactive</status>
I am using the following code:
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT) {
$element = $reader->name;
//$elementarray[] = $element;
echo $element;
if ($reader->depth <1){echo"</br>";}
}
if ($reader->nodeType == XMLReader::TEXT) {
$value = $reader->value;
$valuearray[] = $value;
echo " : ".$value."</br>";
}
}
This outputs
1stElement2ndelement : text
I cannot find anyway in the poorly documented XML Reader to have the output
1stElement:
2ndelement: text
which I can populate an array with.
Any help is appreciated as I am new to PHP.
Do not post pseudo xml. This is not valid xml (element names can not start with a digit). It makes it difficult to understand/validate your problem. Is DOM available? It would be strange to have only XMLReader and not DOM. With DOM you have Xpath and that is a lot easier to use.
So let's assume some options xml.
$xml = <<<'XML'
<options>
<one>1</one>
<two>2</two>
</options>
XML;
XML reader will read each node. Remember the name of the last element node. If a text node is found and not empty add the content with the remembered name to a result array.
$reader = new XMLReader();
$reader->open('data:/text/plain,'.urlencode($xml));
$result = [];
$element = null;
while ($reader->read()) {
switch ($reader->nodeType) {
case XMLReader::ELEMENT :
$element = $reader->name;
break;
case XMLReader::TEXT :
$value = $reader->value;
if (trim($value) != '') {
$result[$element] = $value;
}
break;
}
}
var_dump($result);
Output:
array(2) {
["one"]=>
string(1) "1"
["two"]=>
string(1) "2"
}
In DOM it is possible to just select the element nodes into a list and iterate them:
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$result = [];
foreach ($xpath->evaluate('/options/*') as $node) {
$result[$node->localName] = $node->nodeValue;
}
var_dump($result);
Demo: https://eval.in/170803
DOMXpath::evaluate() allows you make your expressions relative to a context node and to fetch scalars. So you mostly will either fetch a scalar or a node list you can iterate. The result depends on the Xpath expression, so you will always know.
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
foreach ($xpath->evaluate('//channels/channel') as $channel) {
var_dump(
$xpath->evaluate('string(position)', $channel),
$xpath->evaluate('number(1095)', $channel),
$xpath->evaluate('number(1114)', $channel)
);
}
Demo: https://eval.in/172551