How to view DOMNodeList object's data in php - php

when I want to test php array I use the following code
print_r($myarray);
but know I want to see the data of an object
my object is
$xpath = new DOMXPath($doc);
$myobject = $xpath->query('//*[ancestor-or-self::a]');
when I use
print_r($myobject);
I get that output
DOMNodeList Object ( )
I want to iterate through the values of this object to test the result of my query?

DOMNodeList is an interesting object, one that you will not get much information from using print_r or var_dump.
There are many ways to view the data of a DOMNodeList object. Here is an example:
$xpath = new DOMXpath($dom);
$dom_node_list = $xpath->query($your_xpath_query);
$temp_dom = new DOMDocument();
foreach($dom_node_list as $n) $temp_dom->appendChild($temp_dom->importNode($n,true));
print_r($temp_dom->saveHTML());
(Of course use saveXML instead of saveHTML if you are dealing with XML.)
A DOMNodeList can be iterated over like an array. If you want to pull the data out of the DOMNodeList object and put it into a different data structure, such as an array or stdClass object, then you simply iterate through the "nodes" in the DOMNodeList, converting the nodes' values and/or attributes (that you want to have available) before adding them to the new data structure.

It's possible to navigate through the nodes by using a simple foreach as follow:
foreach ($myobject as $node) {
echo $node->nodeValue, PHP_EOL;
} // end foreach
Hope that it can help others, the important pieces of code are the
foreach
and the item
$node->nodeValue
for more details regarding this class please visit:
http://php.net/manual/en/class.domnodelist.php

Someone wrote a great getArray() function:
http://www.php.net/manual/en/class.domdocument.php#101014

Your xpath query is not matching anything in your XML.
From the DomXPath::query manual page:
Returns a DOMNodeList containing all
nodes matching the given XPath
expression . Any expression which do
not return nodes will return an empty
DOMNodeList.

How about a recursive function?
Function XMLPrint_r($d_DomNode) {
print $d_DomNode->$nodeName." ".$d_DomNode->$nodeValue."<br>";
Foreach($d_DomNode->$childNodes as $d_ChildNode) {
print " ";
XMLPrint_r($d_ChildNode);
}
}
I did not test this, but you get the idea.

For some reason, I've been unable to get the saveHTML/saveXML methods to work. So I wrote my own recursive routine which works for me:
function pvIndent ( $ind ) {
for ($i=0;$i<$ind;$i++)
print ( " " );
}
function pvPrint_r ( $val ) {
echo '<pre>';
print_r ( $val );
echo '</pre>';
}
function pvDOMNodeListPrint_r_ ( $ind,$DOMNodeList ) {
for ($item=0;$item<$DOMNodeList->length;$item++) {
$DOMNode = $DOMNodeList->item($item);
if ($DOMNode->nodeName != "#text") {
pvIndent ( $ind );
print $DOMNode->nodeName;
if ($DOMNode->nodeValue)
print " = " . trim($DOMNode->nodeValue);
print "\n";
if ($DOMNode->attributes)
for ($attr=0;$attr<$DOMNode->attributes->length;$attr++) {
$DOMNodeAttr = $DOMNode->attributes->item($attr);
pvIndent ( $ind+1 );
print "#" . $DOMNodeAttr->nodeName . " = " . trim($DOMNodeAttr->nodeValue) . "\n";
}
if ($DOMNode->childNodes)
pvDOMNodeListPrint_r_ ( $ind+1,$DOMNode->childNodes );
}
}
}
function pvDOMNodeListPrint_r ( $DOMNodeList ) {
echo '<pre>';
pvDOMNodeListPrint_r_ ( 0,$DOMNodeList );
echo '</pre>';
}
Call pvDOMNodeListPrint_r with your result from a query on an XDOMPath object.
Notes :
pv is just the prefix I use to avoid name space pollution - feel free to edit it out.
pre tags are used so white space and newlines are handle properly for formatting when output in the body of your html, which is where I generally need such debugging statements - you can format to your taste.
I've explicitly skipped DOMNode's with the name "#text" as these seem to repeat the text already contained in the parent node. I'm not sure this correct for all valid XDOMPath's loaded with HTML, but I've not yet seen an exception - you can always eliminate the exclusion if you don't mind the usual redundancy.

A bit late in the game, but perhaps it helps someone...
Be aware of utf-8 output when using the dom/xpath object itself.
If you would output the nodeValue directly, you would get corrupted characters e.g.:
ìÂÂì ë¹Â디ì¤
ìì ë¹ë””ì¤ í°ì íì¤
You have to load your dom object with the second param "utf-8", new \DomDocument('1.0', 'utf-8'), but still when you print the dom node list/element value you get broken characters:
echo $contentItem->item($index)->nodeValue
you have to wrap it up with utf8_decode:
echo utf8_decode($contentItem->item($index)->nodeValue)
//output: 者不終朝而會,愚者可浹旬而學

var_dump($myobject); may be what you're looking for

its a example of xml file load by xpath
my xml file name is 'test.xml'
<college>
<student>
<firstName>Azhar Uddin</firstName>
<lastName>Raihan</lastName>
<mobile>018*******</mobile>
<fatherName>alam uddin</fatherName>
<address>
<presentAddress title="notun" type="multiple">
<zila>Feni</zila>
<upzila>chhagalniya</upzila>
<post>3912</post>
</presentAddress>
<permanentAddress>
<zila>comilla</zila>
<upzila>sadar</upzila>
</permanentAddress>
</address>
</student>
</college>
now load it
$sxe=simplexml_load_file('test.xml');
$address = $sxe->xpath("student/address/presentAddress");
foreach($address as $addr)
{
foreach($addr as $key=>$val)
{
echo $key."=".$val,"<br>";
}
}

After much debugging I found out that all DOM objects are invisible to var_dump() and print_r(), my guess is because they are C objects and not PHP objects. So I tried saveXML(), which works fine on DOMDocument, but is not implemented on DOMElement.
The solution is simple (if you know it):
$xml = $domElement->ownerDocument->saveXML($domElement);

Related

In the second foreach loop strpos not working proberly [duplicate]

Let's say I have some XML like this
<channel>
<item>
<title>This is title 1</title>
</item>
</channel>
The code below does what I want in that it outputs the title as a string
$xml = simplexml_load_string($xmlstring);
echo $xml->channel->item->title;
Here's my problem. The code below doesn't treat the title as a string in that context so I end up with a SimpleXML object in the array instead of a string.
$foo = array( $xml->channel->item->title );
I've been working around it like this
$foo = array( sprintf("%s",$xml->channel->item->title) );
but that seems ugly.
What's the best way to force a SimpleXML object to a string, regardless of context?
Typecast the SimpleXMLObject to a string:
$foo = array( (string) $xml->channel->item->title );
The above code internally calls __toString() on the SimpleXMLObject. This method is not publicly available, as it interferes with the mapping scheme of the SimpleXMLObject, but it can still be invoked in the above manner.
You can use the PHP function
strval();
This function returns the string values of the parameter passed to it.
There is native SimpleXML method SimpleXMLElement::asXML
Depending on parameter it writes SimpleXMLElement to xml 1.0 file or just to a string:
$xml = new SimpleXMLElement($string);
$validfilename = '/temp/mylist.xml';
$xml->asXML($validfilename); // to a file
echo $xml->asXML(); // to a string
Another ugly way to do it:
$foo = array( $xml->channel->item->title."" );
It works, but it's not pretty.
The accepted answer actually returns an array containing a string, which isn't exactly what OP requested (a string).
To expand on that answer, use:
$foo = [ (string) $xml->channel->item->title ][0];
Which returns the single element of the array, a string.
To get XML data into a php array you do this:
// this gets all the outer levels into an associative php array
$header = array();
foreach($xml->children() as $child)
{
$header[$child->getName()] = sprintf("%s", $child);
}
echo "<pre>\n";
print_r($header);
echo "</pre>";
To get a childs child then just do this:
$data = array();
foreach($xml->data->children() as $child)
{
$header[$child->getName()] = sprintf("%s", $child);
}
echo "<pre>\n";
print_r($data);
echo "</pre>";
You can expand $xml-> through each level until you get what you want
You can also put all the nodes into one array without the levels or
just about any other way you want it.
Not sure if they changed the visibility of the __toString() method since the accepted answer was written but at this time it works fine for me:
var_dump($xml->channel->item->title->__toString());
OUTPUT:
string(15) "This is title 1"
Try strval($xml->channel->item->title)
There is native SimpleXML method SimpleXMLElement::asXML Depending on parameter it writes SimpleXMLElement to xml 1.0 file, Yes
$get_file= read file from path;
$itrate1=$get_file->node;
$html = $itrate1->richcontent->html;
echo $itrate1->richcontent->html->body->asXML();
print_r((string) $itrate1->richcontent->html->body->asXML());
Just put the ''. before any variable, it will convert into string.
$foo = array( ''. $xml->channel->item->title );
The following is a recursive function that will typecast all single-child elements to a String:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION - CLEAN SIMPLE XML OBJECT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function cleanSimpleXML($xmlObject = ''){
// LOOP CHILDREN
foreach ($xmlObject->children() as $child) {
// IF CONTAINS MULTIPLE CHILDREN
if(count($child->children()) > 1 ){
// RECURSE
$child = cleanSimpleXML($child);
}else{
// CAST
$child = (string)$child;
}
}
// RETURN CLEAN OBJECT
return $xmlObject;
} // END FUNCTION

SimpleXML, iteration on all element

I have a stock report file (coming from an outer source, therefore I can't modify in any way) and I would like to iterate over all elements (I have to save them into a MySQL table). As I see the $xml->Stockfile is an array of objects (2 items), so I tried to put it into an array.
For some reason the $myarray contains only the first element after the $myarray = $xml->StockFile assignment.
here is my code:
$xml = simplexml_load_file("../docs/stock.xml");
print_r($xml);
$myarray = $xml->StockFile;
print_r($myarray);
stock.xml:
<NewDataSet>
<StockFile>
<MatrixID>1533</MatrixID>
<Brand>myBrand</Brand>
<ProductCode>001</ProductCode>
<RRP>29.99</RRP>
<Image2Name />
<Image3Name />
</StockFile>
<StockFile>
<MatrixID>1534</MatrixID>
<Brand>myBrand</Brand>
<ProductCode>002</ProductCode>
<RRP>29.99</RRP>
<Image2Name />
<Image3Name />
</StockFile>
</NewDataSet>
Why I'm getting only one item instead of all?
What should I do do retrieve the whole array?
Take care with SimpleXMLElement. It has a lot of magic. Know the magic or get puzzled by print_r or var_dump or similar output. Your example extended:
$myarray = $xml->StockFile;
print_r($myarray); # shows one element
# foreach has both elements:
foreach($myarray as $name => $stockfile)
{
echo $name, ":\n", $stockfile->asXML(), "\n\n";
}
Even though it is the same variable ($myarray) it behaves differently depending on context it is used in. Inside a foreach the SimpleXMLElement (that is the type of that object) will provide an iterator over the child-elements named StockFile as specified here:
$myarray = $xml->StockFile;
However using that variable in some kind of single context, it will for example return the inner string of the first child-element with that name:
echo $myarray, "\n";
(which in your case is just some lines of whitespace).
See Demo: https://eval.in/83787
Running into this "trap" by SimpleXML is actually pretty common. I suggest to understand the basic usage by the example given in the manual:
Basic SimpleXML usage
change the last two lines to
foreach ($xml->StockFile as $nextStockFile) {
print_r ($nextStockFile);
}

How to echo a DOMNodeList Object and a DOMElement Object?

I am using a loop to populate an array called $list. it is working like a charm..
// $content is a DOMNodeList Object
// $value is a DOMElement Object
$list = array();
foreach ($content as $value){
array_push($list, 'title'=>$value->nodeValue);
}
Eventhough my loop is populating my array correctly, I would like to digg into that DOM thing a little more to understand things better (this DOM thing is to new to me...). So what I would like, is to see how the DOMNodeList Object ($content) and DOMElement Object ($value) looks like.
So my question is simple: how can I "echo-out" those 'elements'?
Better than "echo-out" DomElement, read the documentation: http://php.net/manual/en/class.domelement.php.
If you want to see XML representation, use http://www.php.net/manual/en/domnode.c14n.php,
i.e.
echo $value->c14N(false,true);

reading the first value using simplexml

I am using simplexml to read all the child nodes successfully. But how do I read the "NumCrds"?
<ACCOUNT NumCrds="1">
<ACCNO>some Bank</ACCNO>
<CURRCODE>CAD</CURRCODE>
<ACCTYPE>00</ACCTYPE>
</ACCOUNT>
I have read it somewhere in the PHP manual but I am unable to find it now.
$my_num_cards=$sxe->ACCOUNT['NumCrds'];
This is printing the number 1 for all the records even if there are values like 2, 3 in the file.
Attributes can be accessed using array indexes:
$data = '<ACCOUNT NumCrds="1">
<ACCNO>some Bank</ACCNO>
<CURRCODE>CAD</CURRCODE>
<ACCTYPE>00</ACCTYPE>
</ACCOUNT>
';
$xml = new SimpleXMLElement($data);
// this outputs 1
echo $xml['NumCrds'];
It is also possible to use the SimpleXMLElement::attributes() function to returns a list of all of the attribute key/value pairs.
$attributes = $xml->attributes();
echo $attributes['NumCrds'];
Use either $attrs = $el->attributes(); echo $attrs['NumCrds'] or just echo $el['NumCrds']. Attributes are reflected as array elements, while sub-tags are reflected as object properties.
$my_num_cards=$item->attributes()->NumCrds;
This is what I was looking for. Thanks for all your help.
http://fr.php.net/manual/en/simplexmlelement.attributes.php#94433

SimpleXml to string

Is there any function that makes string from PHP SimpleXMLElement?
You can use the SimpleXMLElement::asXML() method to accomplish this:
$string = "<element><child>Hello World</child></element>";
$xml = new SimpleXMLElement($string);
// The entire XML tree as a string:
// "<element><child>Hello World</child></element>"
$xml->asXML();
// Just the child node as a string:
// "<child>Hello World</child>"
$xml->child->asXML();
You can use casting:
<?php
$string = "<element><child>Hello World</child></element>";
$xml = new SimpleXMLElement($string);
$text = (string)$xml->child;
$text will be 'Hello World'
You can use the asXML method as:
<?php
// string to SimpleXMLElement
$xml = new SimpleXMLElement($string);
// make any changes.
....
// convert the SimpleXMLElement back to string.
$newString = $xml->asXML();
?>
Actually asXML() converts the string into xml as it name says:
<id>5</id>
This will display normally on a web page but it will cause problems when you matching values with something else.
You may use strip_tags function to get real value of the field like:
$newString = strip_tags($xml->asXML());
PS: if you are working with integers or floating numbers, you need to convert it into integer with intval() or floatval().
$newNumber = intval(strip_tags($xml->asXML()));
You can use ->child to get a child element named child.
This element will contain the text of the child element.
But if you try var_dump() on that variable, you will see it is not actually a PHP string.
The easiest way around this is to perform a strval(xml->child);
That will convert it to an actual PHP string.
This is useful when debugging when looping your XML and using var_dump() to check the result.
So $s = strval($xml->child);.
Here is a function I wrote to solve this issue (assuming tag has no attributes). This function will keep HTML formatting in the node:
function getAsXMLContent($xmlElement)
{
$content=$xmlElement->asXML();
$end=strpos($content,'>');
if ($end!==false)
{
$tag=substr($content, 1, $end-1);
return str_replace(array('<'.$tag.'>', '</'.$tag.'>'), '', $content);
}
else
return '';
}
$string = "<element><child>Hello World</child></element>";
$xml = new SimpleXMLElement($string);
echo getAsXMLContent($xml->child); // prints Hello World
Sometimes you can simply typecast:
// this is the value of my $xml
object(SimpleXMLElement)#10227 (1) {
[0]=>
string(2) "en"
}
$s = (string) $xml; // returns "en";
Probably depending on the XML feed you may/may not need to use __toString(); I had to use the __toString() otherwise it is returning the string inside an SimpleXMLElement. Maybe I need to drill down the object further ...

Categories