var_dump for DOMNodeList printout unexpected result - php

I have a xml file I load it as the following:
//$file the file system path of the xml file
function getTopicsList($file){
$doc = new DOMDocument();
$doc->load( $file );
var_dump($doc->getElementsByTagName('topic'));
return $doc->getElementsByTagName('topic');
}
The loaded xml file contents is something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<topics>
<topic>
<title>Title1</title>
<keywords>"Some Keys"</keywords>
</topic>
<topic>
<title>The Title</title>
<keywords>Another Key</keywords>
</topic>
<topic>
<title>A Title</title>
<keywords>Key two</keywords>
</topic>
</topics>
The var_dump() in the above code just printout limited information such as:
object(DOMNodeList)#30 (1) {
["length"]=>
int(3)
}
I expected that it should print at least the properties of that object i.e the xml tags and its values. I tried to use other functions such as print_r() and var_export() but there is no details I want.

No, this is node list. You can iterate it with foreach or access nodes using the item() method.
Node lists are used at different places, getElementsByTagName() is one, another is the $childNodes property. Xpath expressions return node lists, too.
Be aware that the nodes can be not only elements but several node types. Like text, cdata section or attribute.
You can use var_dump() to dump a single node. This works with PHP >= 5.3.11 or >= 5.4.1.
$dom = new DOMDocument();
$dom->loadXML('<foo/>');
var_dump($dom->documentElement);
Output:
object(DOMElement)#2 (18) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
string(3) "foo"
["textContent"]=>
string(0) ""
["baseURI"]=>
string(1) "/"
["localName"]=>
string(3) "foo"
["prefix"]=>
string(0) ""
["ownerDocument"]=>
...

Related

XML Selecting data by attribute in PHP [duplicate]

This question already has answers here:
SimpleXML: Selecting Elements Which Have A Certain Attribute Value
(2 answers)
Closed 2 years ago.
I'm not sure if this is even the best way to do this, but I have the following XML document and using PHP I need to select the data that comes in stage one
<?xml version="1.0" encoding="utf-8"?>
<stages>
<stage name="one">
<watch>
<story>
<title>title</title>
<copy>text goes here</copy>
</story>
</watch>
</stage>
<stage name="two">
<watch>
<story>
<title>title 2</title>
<copy>text goes here</copy>
</story>
</watch>
</stage>
</stages>
This PHP returns NULL
$xml = simplexml_load_file("content/content.xml");
var_dump($xml->stage->name['one']);
Stage does not have a name property, it is an attribute.
One option is to use xpath and get the stage(s) where the attribute value for key name is one:
/stages/stage[#name='one']
For example
$xml = simplexml_load_file("content/content.xml");
$result = $xml->xpath("/stages/stage[#name='one']");
var_dump($result);
Output
array(1) {
[0]=>
object(SimpleXMLElement)#2 (2) {
["#attributes"]=>
array(1) {
["name"]=>
string(3) "one"
}
["watch"]=>
object(SimpleXMLElement)#3 (1) {
["story"]=>
object(SimpleXMLElement)#4 (2) {
["title"]=>
string(5) "title"
["copy"]=>
string(14) "text goes here"
}
}
}
}
Perhaps the XML should be like this instead
<stages>
<one>
<watch>
<story>
<title>title</title>
<copy>text goes here</copy>
</story>
</watch>
</one>
<two>
<watch>
<story>
<title>title 2</title>
<copy>text goes here</copy>
</story>
</watch>
</two>
</stages>

PHP XML Read Multi Nodes

i have a xml like below. How can parse this? i don't know how i can do this?
OZELLIK and DEGER is diffrent sometimes 5 sometimes 10. Please help me.
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<STOKLAR>
<STOK>
<SKU>1234</SKU>
<OZELLIKLER>
<OZELLIK>Ekran Kartı Belleği </OZELLIK>
<DEGER>Paylaşımlı </DEGER>
</OZELLIKLER>
</STOK>
<STOK>
<SKU>1454</SKU>
<OZELLIKLER>
<OZELLIK>İşlemci Üreticisi </OZELLIK>
<DEGER>Intel </DEGER>
<OZELLIK>İşlemci Tipi </OZELLIK>
<DEGER>Intel Core i5 </DEGER>
</OZELLIKLER>
</STOK>
</STOKLAR>
</ROOT>
It isn't that difficult with DOM and Xpath expressions:
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// iterate STOK element nodes
foreach ($xpath->evaluate('/ROOT/STOKLAR/STOK') as $stok) {
// fetch first SKU child element node as string
var_dump($xpath->evaluate('string(SKU)', $stok));
// iterate OZELLIK element nodes in OZELLIKLER
foreach ($xpath->evaluate('OZELLIKLER/OZELLIK', $stok) as $ozellik) {
var_dump(
// content of current OZELLIK
$ozellik->textContent,
// first following sibling element node, if DEGER, as string
$xpath->evaluate('string((./following-sibling::*)[1][self::DEGER])', $ozellik)
);
}
}
Output:
string(4) "1234"
string(22) "Ekran Kartı Belleği "
string(14) "Paylaşımlı "
string(4) "1454"
string(21) "İşlemci Üreticisi "
string(6) "Intel "
string(15) "İşlemci Tipi "
string(14) "Intel Core i5 "
DOMXpath::evaluate() can return a node list or a scalar value depending on the expression. The second argument sets the context node for the expression. Here is an explanation of the last (most complex) expression:
Get the following sibling element nodesfollowing-sibling::*
Limit to the first found node(following-sibling::*)[1]
Filter by node name DEGER(following-sibling::*)[1][self::DEGER]
Return text content of this node, empty string if no node was foundstring((following-sibling::*)[1][self::DEGER])
By default expressions work on the "child" axis. The expression uses two other axes "following-sibling" and "self" to look for the required nodes.

Extract Data off multiple XML row

I have a really weird XML response and i need to extract it's data. I need to get the data in the "value" attribute but i need to choose them according to their "key" attributes.
This is how it looks like
<phone>
2125556666
</phone>
<State>
ny
</State>
<Response>
<data key="Supported" value="Yes"/>
<data key="Host" value="Remote"/>
<data key="WholeProductList">
<data key="Product" value="a-z44"/>
<data key="Product" value="c-k99"/>
<data key="Product" value="e-b089"/>
<data key="Product" value="z-p00"/>
<data key="Product" value="r-333"/>
<data key="Product" value="t-RS232"/>
<data key="Product" value="4-lve"/>
<data key="Product" value="Shutdown"/>
</data>
</Response>
In PHP i currenty have
$xmltmp = new DomDocument;
$xmltmp->loadXml($response);
$phone = $xmlresponse->getElementsByTagName('phone')->item(0)->nodeValue;
$state = $xmlresponse->getElementsByTagName('state')->item(0)->nodeValue;
echo $phone;
echo $state;
This currently outputs both phone number and state. It works fine.
Now i need to know if the "Supported" key's value is Yes or No, and if it's Yes, i need to get all "Products". I'm kinda stuck because i am having a hard time making the foreach statement and then checking the "key" attribute value.
Thanks!
Your XML is invalid. An XML document always needs a single document element node.
Example:
<root>
<phone>2125556666</phone>
<State>ny</State>
<Response>
<data key="Supported" value="Yes"/>
...
</data>
</Response>
</root>
The easiest way to fetch data from a DOM is XPath. In PHP that is provided by the DOMXPath class and part of the ext/dom. DOMXPath::evaluate() allows you to fetch node lists or scalar values from the DOM document.
$dom = new DOMDocument;
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$phone = $xpath->evaluate('string(/*/phone)');
$state = $xpath->evaluate('string(/*/State)');
var_dump($phone, $state);
Output:
string(10) "2125556666"
string(2) "ny"
An expression like /*/phone selects all phone element child nodes inside the document element. string(/*/phone) casts the first found node into a string and return that. If no node was found, it will return an empty string.
The XPath expression for the supported status is slightly more complex. Conditions for nodes are provided in []. It is possible to compare the result directly in XPath. The return value will be an boolean.
$supported = $xpath->evaluate('/*/Response/data[#key="Supported"]/#value = "Yes"');
var_dump($supported);
Output:
bool(true)
If the expression returns a node list you can iterate it with foreach().
$nodes = $xpath->evaluate(
'/*/Response/data[#key="WholeProductList"]/data[#key="Product"]/#value'
);
$products = [];
foreach ($nodes as $attributeNode) {
$products[] = $attributeNode->value;
}
var_dump($products);
Output:
array(8) {
[0]=>
string(5) "a-z44"
[1]=>
string(5) "c-k99"
[2]=>
string(6) "e-b089"
[3]=>
string(5) "z-p00"
[4]=>
string(5) "r-333"
[5]=>
string(7) "t-RS232"
[6]=>
string(5) "4-lve"
[7]=>
string(8) "Shutdown"
}
This won't quite work "as is" since I don't know what the actual structure of the XML document is, but in short you map the XML nodes to XPath like //root/node/child_node/#attribute and so on.
It should also have some sanity (not null) type checking in.
$xmltmp = new DomDocument;
$xmltmp->loadXml($response);
$xQuery = new DOMXPath($xmltmp);
//not sure what your root node is so the query path is probably wrong
$supported = $xQuery->query('/Response/data[#key="Supported"]/#value')->value;
You can also replace:
$phone = $xmlresponse->getElementsByTagName('phone')->item(0)->nodeValue;
$state = $xmlresponse->getElementsByTagName('state')->item(0)->nodeValue;
With something like (again - without the full structure of the XML document the path itself is probably not quite right):
$phone = $xQuery->query('/phone')->item(0)->nodeValue;
$state = $xQuery->query('/State')->item(0)->nodeValue;

Xpath for a REST response with Keys

I am trying to use Xpath to find a username value from a REST response. When I use
$data = new simpleXMLElement($response->getBody());
$matches = $data->xpath('//KEY[#name="username"]');
I get
array(1) {
[0]=>
object(SimpleXMLElement)#7 (2) {
["#attributes"]=>
array(1) {
["name"]=>
string(8) "username"
}
["VALUE"]=>
string(5) "guest"
}
}
My question is, what is the Xpath expression to get only the value to display? in this example it is guest, but will change depending on the user.
Any suggestions greatly appreciated.
XML structure below.
<?xml version="1.0" encoding="UTF-8" ?>
<RESPONSE>
<MULTIPLE>
<SINGLE>
<KEY name="id">
<VALUE>1</VALUE>
</KEY>
<KEY name="username">
<VALUE>guest</VALUE>
</KEY>
<KEY name="firstname">
<VALUE>Guest user</VALUE>
</KEY>
Not possible with SimpleXML. The xpath method will always return an Array.
But you can do:
$keys = simplexml_load_string($xml);
echo current($keys->xpath('//KEY[#name="username"]/VALUE'));
demo
Or use DOM. The following will return the string immediately:
$dom = new DOMDocument;
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
echo $xpath->evaluate('string(//KEY[#name="username"]/VALUE)');
demo
I am not sure if simpleXml can evaluate this expression, but any compliant XPath implementation will:
string(//KEY[#name="username"]/VALUE)
When this expression is evaluated, the result is the string value of the Value element that is a child of the (first in document order) KEY element that has a name attribute, whose string value is "username".
Also, if the structure of the XML document is known, then it may be more efficient (fast) not to use //:
string(/RESPONSE/MULTIPLE/SINGLE/KEY[#name="username"]/VALUE)

PHP, SimpleXML arrays. Unanticipated casting of an array to a string

Sample XML:
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
The following code is the basis for my small application, it works as would be expected:
<?php
// $xml is some simplexml object
sizeof($xml->ratings); //3
foreach($xml->ratings as $rating){
echo($rating->value."::"); //this echoes all 3 rating values: 3::5::7
}
?>
This next code, which I would normally consider to be equivalent is not. And I have no idea why:
<?php
// $xml is some simplexml object
$ratings = $xml->ratings;
sizeof($ratings); //3, all is well so far
foreach($ratings as $rating){
echo($rating."::");
/*this echoes a never-ending list of ratings,
looking like 3::5::5::5::5::5::5::5...... */
}
?>
My feeling is that the assignment operator is casting the array of simplexml objects (ratings objects) as something odd, but have no clue as to how.
Other little hints:
var_dump($xml);
/* Output is:
object(SimpleXMLElement)#7 (1) {
["ratings"]=>
array(3) {
[0]=>
string(1) "3"
[1]=>
string(1) "5"
[2]=>
string(1) "7"
}
}
*/
var_dump($ratings);
/* Output is:
object(SimpleXMLElement)#6 (1) {
[0]=>
string(1) "3"
}
*/
Your echos are not the same:
echo($rating."::");
should be
echo($rating->value."::");
Ok, cleaning up some of my own work. After attempting to simplify my issue more, I was not able to prove it. After messing with the actual code, I assume this means I have some sort of mutating object elsewhere in my app that is going bonkers and creating weird results in this xml parsing. Sorry for the confusion and needless question (I guess this proves why i'm trying to refactor some of my complexity out of this app).
As a parting gift, here is the test-suite of code that I used (from simple to more realistic) that I used to prove that all worked as advertised:
<?php
$string = <<<XML
<?xml version='1.0'?>
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
XML;
$xml = simplexml_load_string($string);
var_dump($xml);
echo("Size:".sizeof($xml->ratings)."\n");
foreach($xml->ratings as $rating){
echo($rating."::");
}
echo("\n"."------"."\n");
$ratings = $xml->ratings;
echo("Size:".sizeof($ratings)."\n");
foreach($ratings as $rating){
echo($rating."::");
}
echo("\n\n\n\n"."||||New Example||||"."\n\n\n\n");
$stringthree = <<<XML
<root attr1="val" attr2="desc">
<field-one>val</field-one>
<elm-two attr-name="foo">elmTwoVal1</elm-two>
<elm-three>elmThreeVal1</elm-three>
<elm-two attr-name="bar">elmTwoVal2</elm-two>
<elm-three>elmThreeVa2</elm-three>
<elm-two attr-name="bear">elmTwoVal3</elm-two>
<elm-three>elmThreeVal3</elm-three>
</root>
XML;
$xmlthree = simplexml_load_string($stringthree);
var_dump($xmlthree);
echo("Size:".sizeof($xmlthree->{'elm-two'})."\n");
foreach($xmlthree->{'elm-two'} as $elm){
echo($elm."::");
}
echo("\n"."------"."\n");
$elmMain = $xmlthree->{'elm-two'};
echo("Size:".sizeof($elmMain)."\n");
foreach($elmMain as $elm){
echo($elm."::");
}
?>

Categories