XML Node accessing attribute with namespace - php

This is my xml data.
<?xml version="1.0" encoding="UTF-8"?>
<ns1:catalog
xmlns:ns1="http://www.omnichannelintegrationlayer.com/xml/catalog/2016-01-01" catalog-id="at-master-catalog">
<ns1:product product-id="4132002004">
<ns1:min-order-quantity>1</ns1:min-order-quantity>
<ns1:step-quantity>1</ns1:step-quantity>
<ns1:short-description
xmlns:ns2="xml" ns2:lang="de-AT">Jogginghose Cacy jr
</ns1:short-description>
<ns1:short-description
xmlns:ns2="xml" ns2:lang="de-CH">Jogginghose Cacy jr
</ns1:short-description>
</ns1:product>
I'm trying to filter the xml the short-description base on ns2:lang attribute.
This is what I've done so far:
foreach ($xml->xpath("//ns1:product[#product-id='".$productid."']/ns1:short-description/") as $short_description) {
$namespaces = $short_description->getNameSpaces(true);
$ns1 = $short_description->children($namespaces['ns1']);
$ns2 = $short_description->children($namespaces['ns2']);
var_dump($ns2);
echo $ns2["lang"];
}
The output of var_dump looks okay:
object(SimpleXMLElement)#27 (1) { ["#attributes"]=> array(1) { ["lang"]=> string(5) "de-AT" } }
But I can't access the array because when I echo $ns2["lang"], I'm getting NULL.
I already tried different solution like declaring namespace first but no luck.
Thanks in advance.

The values you are looking for are in the attributes and the attributes use a namespace which you can pass as a parameter to the attributes method.
The attribute itself is of type SimpleXMLElement and has a method __toString to get the text content that is directly in this element.
You could for example use echo $short_description->attributes($namespaces['ns2'])->lang; or cast it to a (string)
You might update your code to:
$namespaces = $xml->getNamespaces(true);
foreach ($xml->xpath("//ns1:product[#product-id='".$productid."']/ns1:short-description") as $short_description) {
$langAsString = (string)$short_description->attributes($namespaces['ns2'])->lang;
echo $langAsString . "<br>";
}
That would give you:
de-AT
de-CH
Demo

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

Getting the value of XML field matching a property via XPATH

I've got a really odd XML schema that's causing me unecessary grief and woe.
I need to get the value of an IMAGEFILENAME node that has a property of "hide".
The XML schema looks something like:
<PHOTOS>
<IMAGETHUMBFILENAME/>
<IMAGECAPTION>
This is a caption
</IMAGECAPTION>
<PRINTQUALITYIMAGE>
/mylocation/filename1.jpg
</PRINTQUALITYIMAGE>
<IMAGEFILENAME pictype="show">
/mylocation/filename2.jpg
</IMAGEFILENAME>
<IMAGETHUMBFILENAME/>
<IMAGECAPTION>This is another caption</IMAGECAPTION>
<PRINTQUALITYIMAGE>
/mylocation/filename3.jpg
</PRINTQUALITYIMAGE>
<IMAGEFILENAME pictype="hide">
/mylocation/filename4.jpg
</IMAGEFILENAME>
<IMAGETHUMBFILENAME/>
</PHOTOS>
And I've managed to come up with the following XPATH using PHP:
$nodes = $xml->xpath('/PHOTOS/IMAGEFILENAME[#pictype="hide"]');
var_dump($nodes);
When I do a dump of the $nodes var what I'd hope to see (and what I want) is to get the value /mylocation/filename4.jpg. Instead what I'm getting is:
array(1) {
[0]=>
object(SimpleXMLElement)#333 (1) {
["#attributes"]=>
array(1) {
["pictype"]=>
string(10) "hide"
}
}
}
I've tried various combinations of /parent, /text() and /node() but with no joy at all.
Please somebody tell me what a muppet I'm being and put me out of my misery. Either that or is the schema being problematic?
So, you have array of SimpleXMLElements.
To get string representation of SimpleXMLElement you can just echo it:
$nodes = $xml->xpath('/PHOTOS/IMAGEFILENAME[#pictype="hide"]');
echo $nodes[0]; // I used `[]` notation to get first element of array
To use string representation of SimpleXMLElement later in your code you can convert it to string explicitly:
$nodes = $xml->xpath('/PHOTOS/IMAGEFILENAME[#pictype="hide"]');
$node_str = strval($nodes[0]); // still `[]` notation

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 ...

php simplexml get object variable start with number

When I parse an rss file with SimpleXMLElement, I get this object :
object(SimpleXMLElement)#307 (1) {
[0]=>
string(39) "http://workspace/wordpress/hello-world/"
}
$var->0 doesn't works.
I don't know how to do it :(
thanks.
It behaves like an array
echo $var[0];
In this case - when there's only one (child) element - you don't even have to use the index
echo $var;

How to view DOMNodeList object's data in 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);

Categories