Get elements from a XML content by PHP - php

I am trying to get elements from this XML content but returns empty:
<results>
<error>
<string>i</string>
<description>Make I uppercase</description>
<precontext></precontext>
<suggestions>
<option>I</option>
</suggestions>
<type>grammar</type>
</error>
</results>
And this is my code to extract element type of grammar :
$dom = new DOMDocument();
$dom->loadXml($output);
$params = $dom->getElementsByTagName('error'); // Find Sections
$k=0;
foreach ($params as $param) //go to each section 1 by 1
{
if($param->type == "grammar"){
echo $param->description;
}else{
echo "other type";
}
Problem is the script returns empty.

you can use simplexml_load_string()
$output = '<results>
<error>
<string>i</string>
<description>Make I uppercase</description>
<precontext></precontext>
<suggestions>
<option>I</option>
</suggestions>
<type>grammar</type>
</error>
</results>';
$xml = simplexml_load_string($output);
foreach($xml->error as $item)
{
//echo (string)$item->type;
if($item->type == "grammar"){
echo $item->description;
}else{
echo "other type";
}
}

You apparently haven't configured PHP to report errors because your code triggers:
Notice: Undefined property: DOMElement::$type
You need to grab <type> the same way you grab <error>, using DOM methods like e.g. getElementsByTagName(). Same for node value:
if ($param->getElementsByTagName('type')->length && $param->getElementsByTagName('type')[0]->nodeValue === 'grammar') {
// Feel free to add additional checks here:
echo $param->getElementsByTagName('description')[0]->nodeValue;
}else{
echo "other type";
}
Demo

I think is this what you want.
<?php
$output = '<results>
<error>
<string>i</string>
<description>Make I uppercase</description>
<precontext></precontext>
<suggestions>
<option>I</option>
</suggestions>
<type>grammar</type>
</error>
</results>';
$dom = new DOMDocument();
$dom->loadXml($output);
$params = $dom->getElementsByTagName('error'); // Find Sections
$k=0;
foreach ($params as $param) //go to each section 1 by 1
{
$string = $param->getElementsByTagName( "string" )->item(0)->nodeValue;
$description = $param->getElementsByTagName( "description" )->item(0)->nodeValue;
$option = $param->getElementsByTagName( "option" )->item(0)->nodeValue;
$type = $param->getElementsByTagName( "type" )->item(0)->nodeValue;
echo $type;
if($type == "grammar"){
echo $description ;
}else{
echo "other type";
}
}
?>

You're mixing DOM with SimpleXML. This is possible, but you would need to convert the DOM element node into a SimpleXML instance with simplexml_import_dom().
Or you use Xpath. getElementsByTagName() is a low level DOM method. Using Xpath expressions allows for more specific access with a lot less code.
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('//error') as $error) {
var_dump(
[
'type' => $xpath->evaluate('string(type)', $error),
'description' => $xpath->evaluate('string(description)', $error)
]
);
}
Output:
array(2) {
["type"]=>
string(7) "grammar"
["description"]=>
string(16) "Make I uppercase"
}
Xpath expressions allow for conditions as well, for example you could fetch all grammar errors using //error[#type = "grammar"].

Related

XMLReader not reading cdata

I have a problem. I wrote this code but I can't read <![CDATA[Epsilon Yayınları]]>. Items with cdata, when I get them it's empty. Is there an alternative solution?
XML:
<urunler>
<urun>
<stok_kod>9789753314930</stok_kod>
<urun_ad><![CDATA[Kırmızı Erik]]></urun_ad>
<Barkod>9789753314930</Barkod>
<marka><![CDATA[Epsilon Yayınları]]></marka>
<Kdv>8,00</Kdv>
<satis_fiyat>9,5000</satis_fiyat>
<kat_yolu><![CDATA[Edebiyat>Hikaye]]></kat_yolu>
<resim>http://basaridagitim.com/images/product/9789753314930.jpg</resim>
<Yazar>Tülay Ferah</Yazar>
<Bakiye>2,00000000</Bakiye>
<detay><![CDATA[]]></detay>
</urun>
</urunler>
$xml = new XMLReader;
$xml->open(DIR_DOWNLOAD . 'xml/'.$xml_info['xml_file_name']);
$doc = new DOMDocument;
$product_data = array();
$i=0;
while ($xml->read() && $xml->name !== 'urun');
while ($xml->name === 'urun') { $i++;
$node = simplexml_import_dom($doc->importNode($xml->expand(), true));
var_dump($node->urun_ad); die();
Dump print:
object(SimpleXMLElement)#143 (1) {
[0]=>
object(SimpleXMLElement)#145 (0) {
}
}
It just comes down to how your printing out the value. If you change the var_dump to either of the following, you will get what your after...
//var_dump($node->urun_ad)
echo $node->urun_ad.PHP_EOL;
echo $node->urun_ad->asXML().PHP_EOL;
outputs...
Kırmızı Erik
<urun_ad><![CDATA[Kırmızı Erik]]></urun_ad>
One thing to note is that if you want to use the value in another method, you may have to cast it to a string (echo does this automatically). So the first one would be (for example)...
$urun_ad = (string)$node->urun_ad;

How to get CDATA texts from XML by id in tags

I know how to access tags in XML using PHP but this time, I have to use a function getText($textId) to access text content in those tags but I tried so many things that I am desperate for help.
I tried this
$doc->load("localisations_EN.xml");
$texts = $doc->getElementsByTagName("txt");
$elem = $doc->getElementById("home");
$children = $elem->childNodes;
foreach ($children as $child) {
if ($child->nodeType == XML_CDATA_SECTION_NODE) {
echo $child->textContent . "<br/>";
}
}
print_r($texts);
print_r($doc->getElementById('home'));
foreach ($texts as $text)
{
foreach($text->childNodes as $child) {
if ($child->nodeType == XML_CDATA_SECTION_NODE) {
echo $child->textContent . "<br/>";
}
}
}
Then I tried this but I don't know how to access the string value
$xml=simplexml_load_file("localisations_EN.xml") or die("Error: Cannot create object");
print_r($xml);
$description = $xml->xpath("//txt[#id='home']");
var_dump($description);
And I got something like this
array(1) { [0]=> object(SimpleXMLElement)#2 (1) { ["#attributes"]=>
array(1) { ["id"]=> string(4) "home" } } }
This is the XML file I have to use
<?xml version="1.0" encoding="UTF-8" ?>
<localisation application="test1">
<part ID="menu">
<txt id="home"><![CDATA[Home]]></txt>
<txt id="news"><![CDATA[News]]></txt>
<txt id="settings"><![CDATA[Settings]]></txt>
</part>
<part ID="login">
<txt id="id"><![CDATA[Login]]></txt>
<txt id="password"><![CDATA[Password]]></txt>
<txt id="forgetPassword"><![CDATA[Forget password?]]></txt>
</part>
</localisation>
Thanks for your help.
simplexml element has a __toString() magic function that will return the text content of the element (however, not the text content of sub-elements)
so your simplexml code should be
$xml=simplexml_load_file("localisations_EN.xml");
$description = (string) $xml->xpath("//txt[#id='home']")[0];
// ^-- triggers __toString() ^-- xpath returns array
because xpath returns an array of elements, you need to fetch one (or more) and cast it to string. To get the immediate contents of that element.
with DOMDocument:
don't know why you go for the (non-existant) child nodes there. CDATA is just syntax to say "don't parse this, this is data"
$doc = new DOMDocument;
$doc->load("localisations_EN.xml");
$texts = $doc->getElementsByTagName('txt');
foreach($texts as $text) {
if($text->getAttribute('id') == 'home') {
// prepend hasAttribute('id') if needed to if clause above
$description = $text->textContent;
}
}
also, $doc->getElementById() probably only works, if the DTD has set some attribute as ID. Since your xml doesn't do that (it doesn't name a DTD) it doesn't work.
with DOMDocument and DOMXPath
// $doc as before
$xpath = new DOMXPath($doc);
$description = $xpath->evaluate('//txt[#id="home"]')[0]->textContent;
// as before, xpath returns an array, that's why ---^

DOMDocument node with boolean value

What is the correct way to extract a boolean value from an XML node? I have tried with this:
<?php
$xml = "<node><code>false</code></node>";
$dom = new DOMDocument();
$dom->loadXML($xml);
$nodeList = $dom->getElementsByTagName('code');
if ($nodeList->length == 1) {
if($nodeList->item(0)->nodeValue){
echo 'VALID';
} else {
echo 'NOT VALID';
}
}
?>
but I get VALID as a result.
nodeValue is going to return a string, so you need to do a string comparison. For example:
if($nodeList->item(0)->nodeValue != 'false'){
echo 'VALID';
} else {
echo 'NOT VALID';
}
You may also consider using filter_var($string, FILTER_VALIDATE_BOOLEAN) to convert the value to boolean (for example it will also convert "1" or "yes" to a boolean), depending on the type of value you'll get in the XML.
Here is no such thing as a 'boolean' value in XML it is all text. But you can use an Xpath expression to validate the value of a specific node and return it as an boolean directly:
$xml = <<<'XML'
<nodes>
<node>
<code>false</code>
</node>
<node>
<code>true</code>
</node>
</nodes>
XML;
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('//node') as $node) {
var_dump(
$xpath->evaluate('code = "true"', $node)
);
}
Output:
bool(false)
bool(true)
An alternative would be to fetch the value as a string and use filter_var().
foreach ($xpath->evaluate('//node') as $node) {
var_dump(
filter_var($xpath->evaluate('string(code)', $node), FILTER_VALIDATE_BOOLEAN)
);
}
Another solution
( filter_var( $xmlNodeElement->current(), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ) ? echo "Valid" : echo "Not valid";
Current when initialize, show first node element more about follow link

Parse XML to PHP using ID value

How can I echo xml values with php by calling their "columnId" and not the position in the array ? (The array is really long)
Here is a sample of the xml :
<Data>
<Value columnId="ITEMS_SOLD">68</Value>
<Value columnId="TOTAL_SALES">682</Value>
<Value columnId="SHIPPING_READY">29</Value>
...
</Data>
The following php gives me all of the values :
$url = 'XXX';
$xml = file_get_contents($url);
$feed = simplexml_load_string($xml) or die("Error: Cannot create object");
foreach($feed->Data->Value as $key => $value){
echo $value;
}
I would like to be able to use something like that in my document :
echo $feed->Data->Value['TOTAL_SALES'];
Thank you for your help.
echo $feed->Data->Value[1];
I have an another way for your solution. You can convert xml object into array and use this for further process. Try this code:
<?php
$url = 'XXX';
//Read xml data, If file exist...
if (file_exists($url)) {
//Load xml file...
$xml = simplexml_load_file($url);
$arrColumn = array();//Variable initialization...
$arrFromObj = (array) $xml;//Convert object to array...
$i = 0;//Variable initialization with value...
//Loop until data...
foreach($xml AS $arrKey => $arrData) {
$columnId = (string) $arrData['columnId'][0];//array object to string...
$arrColumn[$columnId] = $arrFromObj['Value'][$i];//assign data to array...
$i++;//Incremental variable...
}
} else {//Condition if file not exist and display message...
exit('Failed to open file');
}
?>
Above code will store result into array variable $arrColumn and result is:
Array
(
[ITEMS_SOLD] => 68
[TOTAL_SALES] => 682
[SHIPPING_READY] => 29
)
Hope this help you well!
Use XPath. SimpleXML and DOM support it, but SimpleXML has some limits (It can only fetch node lists).
SimpleXML
$feed = simplexml_load_string($xml);
var_dump(
(string)$feed->xpath('//Value[#columnId = "TOTAL_SALES"]')[0]
);
Output:
string(3) "682"
DOM
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
var_dump(
$xpath->evaluate('string(//Value[#columnId = "TOTAL_SALES"])')
);
Output:
string(3) "682"

ignoring nested elements when parsing xml with php

probably a simple question to answer for someone:::
xml:
<foobar>
<foo>i am a foo</foo>
<bar>i am a bar</bar>
<foo>i am a <bar>bar</bar></foo>
</foobar>
In the above, I want to display all elements that are <foo>. When the script gets to the line with the nested < bar > the result is "i am a bar" .. which isn't the result I had hoped for.
Is it not possible to print out the entire contents of that element as it is, so that i see: "i am a <bar>bar</bar>"
php:
$xml = file_get_contents('sample');
$dom = new DOMDocument;
#$dom->loadHTML($xml);
$resources= $dom->getElementsByTagName('foo');
foreach ($resources as $resource){
echo $resource->nodeValue . "\n";
}
After some trolling and trying to do what I needed with SimpleXML, I arrived at the following conclusion. My issue with SimpleXML was where the elements are. If the xml is structured, and the hierarchy is standard ... I have no problem.
If the XML is a web page for example, and the <foo> element is anywhere, SimpleXML doesn't have a good facility like getElementsByTagName to pull out the element wherever it may be....
<?php
$doc = new DOMDocument();
$doc->load('sample');
$element_name = 'foo';
if ($doc->getElementsByTagName($element_name)->length > 0) {
$resources = $doc->getElementsByTagName($element_name);
foreach ($resources as $resource) {
$id = null;
if (!$resource->hasAttribute('id')) {
$resource->setAttribute('id', gen_uuid());
}
$innerHTML = null;
$children = $resource->childNodes;
foreach ($children as $child) {
$tmp_doc = new DOMDocument();
$tmp_doc->appendChild($tmp_doc->importNode($child,true));
$innerHTML .= rtrim($tmp_doc->saveHTML());
}
$resource->nodevalue = $innerHTML;
}
}
echo $doc->saveHTML();
?>
Rather than writing all that code, you might try XPath. That expression would be "//foo", which would get a list of all the elements in the document named "foo".
http://php.net/manual/en/simplexmlelement.xpath.php

Categories