Simplexml get node by attribute - php

I've got xml file:
<?xml version="1.0" ?>
<xml>
<opis lang="en">My text</opis>
<opis lang="cz">My text2</opis>
</xml>
I want to get "My text2" - so a node where attribute lang is "cz":
$xml = simplexml_load_file($fileName);
$result = $xml->xpath('//xml/opis[#lang="cz"]')
but instead of value I get:
array(1) (
[0] => SimpleXMLElement object {
#attributes => array(1) (
[lang] => (string) cz
)
}
))

You could get the value like this:
$xml = simplexml_load_file($fileName);
$result = $xml->xpath('//xml/opis[#lang="cz"]');
foreach($result as $res) {
echo $res;
}

Try using DomDocument:
$xml = new DomDocument;
$xml->load('yourFile');
$xpath = new DomXpath($xml);
foreach ($xpath->query('//xml/opis[#lang="cz"]') as $rowNode) {
echo $rowNode->nodeValue; // will be 'this item'
}

Related

XML Parsing in PHP (using simplexml_load_string)

I have the following code and I have been working to try to get this working.
<?php declare(strict_types=1);
$session_token = '?'; $xml = '';
$result = '<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://ws.careerbuilder.com/resumes/"><Packet><Errors /><SessionToken>3msk323msd-3312-CQ-2</SessionToken></Packet></string>
';
if ($result) {
$xml = simplexml_load_string($result);
print_r($xml);
if ($xml !== false) {
$session_token = $xml->SessionToken;
echo PHP_EOL.'Session: '. $session_token;
} else {
echo 'Error: XML does NOT appear to be valid';
}
} else
echo 'Error: result does NOT appear be valid';
The problem is no matter what I'm not able to extract the <SessionToken> value from the XML. When I use print_r() I get the following:
SimpleXMLElement Object
(
[0] => <Packet><Errors /><SessionToken>3msk323msd-3312-CQ-2</SessionToken></Packet>
)
Your input is entity-encoded. If this is really what it looks like, you'll need to decode it first:
$xml = simplexml_load_string(html_entity_decode($result));
$token = (string) $xml->Packet->SessionToken[0];
You document contains nested XML. The text content of the string element is serialized XML. So you need to parse it after reading it.
$result = '<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://ws.careerbuilder.com/resumes/"><Packet><Errors /><SessionToken>3msk323msd-3312-CQ-2</SessionToken></Packet></string>
';
$string = new SimpleXMLElement($result);
$packet = new SimpleXMLElement((string)$string);
var_dump($packet);
Output:
object(SimpleXMLElement)#2 (2) {
["Errors"]=>
object(SimpleXMLElement)#3 (0) {
}
["SessionToken"]=>
string(20) "3msk323msd-3312-CQ-2"
}

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"

php DomXPath - how to strip html tags and its contents from nodeValue?

In this code
<root>
<main>
<cont>
<p>hello<a>world</a></p>
<p>hello</p>
<p>hello<a>world</a></p>
</cont>
</main>
</root>
I just need to get only the text inside <cont> tag. without getting <a> tag and its contents
so, the results will be hello hello hello without world
You can select the text nodes that are a direct descendant of each <p> tag:
$dom = new DOMDocument;
$dom->loadXml($xmlData);
$xpath = new DOMXpath($dom);
foreach ($xpath->query('//cont/p/text()') as $text) {
echo $text->textContent, "\n";
}
A simplexml_load_string() or simplexml_load_file() should be enough:
$xml_string = '<root> <main> <cont> <p>hello<a>world</a></p> <p>hello</p> <p>hello<a>world</a></p> </cont> </main></root>';
$xml = simplexml_load_string($xml_string);
$p = $xml->main->cont->p;
foreach($p as $value) {
$parapgraphs[] = (string) $value;
}
echo '<pre>';
print_r($parapgraphs);
Should show something like:
Array
(
[0] => hello
[1] => hello
[2] => hello
)

PHP XML - Find out the path to a known value

Here is an XML bit:
[11] => SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => 46e8f57e67db48b29d84dda77cf0ef51
[label] => Publications
)
[section] => Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => 9a34d6b273914f18b2273e8de7c48fd6
[label] => Journal Articles
[recordId] => 1a5a5710b0e0468e92f9a2ced92906e3
)
I know the value "46e8f57e67db48b29d84dda77cf0ef51" but its location varies across files. Can I use XPath to find the path to this value? If not what could be used?
Latest trial that does not work:
$search = $xml->xpath("//text()=='047ec63e32fe450e943cb678339e8102'");
while(list( , $node) = each($search)) {
echo '047ec63e32fe450e943cb678339e8102',$node,"\n";
}
PHPs DOMNode objects have a function for that: DOMNode::getNodePath()
$xml = <<<'XML'
<root>
<child key="1">
<child key="2"/>
<child key="3"/>
</child>
</root>
XML;
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$nodes = $xpath->evaluate('//child');
foreach ($nodes as $node) {
var_dump($node->getNodePath());
}
Output:
string(11) "/root/child"
string(20) "/root/child/child[1]"
string(20) "/root/child/child[2]"
SimpleXML is a wrapper for DOM and here is a function that allows you to get the DOMNode for an SimpleXMLElement: dom_import_simplexml.
$xml = <<<'XML'
<root>
<child key="1">
<child key="2"/>
<child key="3"/>
</child>
</root>
XML;
$structure = simplexml_load_string($xml);
$elements = $structure->xpath('//child');
foreach ($elements as $element) {
$node = dom_import_simplexml($element);
var_dump($node->getNodePath());
}
To fetch an element by its attribute xpath can be used.
Select all nodes using the element joker anywhere in the document:
//*
Filter them by the id attribute:
//*[#id = "46e8f57e67db48b29d84dda77cf0ef51"]
$dom = new DOMDocument();
$dom->loadXml('<node id="46e8f57e67db48b29d84dda77cf0ef51"/>');
$xpath = new DOMXpath($dom);
foreach ($xpath->evaluate('//*[#id = "46e8f57e67db48b29d84dda77cf0ef51"]') as $node) {
var_dump(
$node->getNodePath()
);
}
Is this string always in the #id attribute? Then a valid and distinct path is always //*[#id='46e8f57e67db48b29d84dda77cf0ef51'], no matter where it is.
To construct a path to a given node, use $node->getNodePath() which will return an XPath expression for the current node. Also take this answer on constructing XPath expression using #id attributes, similar to like Firebug does, in account.
For SimpleXML you will have to do everything by hand. If you need to support attribute and other paths, you will have to add this, this code only supports element nodes.
$results = $xml->xpath("/highways/route[66]");
foreach($results as $result) {
$path = "";
while (true) {
// Is there an #id attribute? Shorten the path.
if ($id = $result['id']) {
$path = "//".$result->getName()."[#id='".(string) $id."']".$path;
break;
}
// Determine preceding and following elements, build a position predicate from it.
$preceding = $result->xpath("preceding-sibling::".$result->getName());
$following = $result->xpath("following-sibling::".$result->getName());
$predicate = (count($preceding) + count($following)) > 0 ? "[".(count($preceding)+1)."]" : "";
$path = "/".$result->getName().$predicate.$path;
// Is there a parent node? Then go on.
$result = $result->xpath("parent::*");
if (count($result) > 0) $result = $result[0];
else break;
}
echo $path."\n";
}

how to print xml data with xpath and php if its matches a string pattern?

I am using php and xpath to display an xml file which is having a xml code like this:
<?xml version="1.0" encoding="utf-8"?>
<cities>
<city>
<city_id>8393</city_id>
<country>ITALY</country>
<name>Petrosino</name>
<establishment_count>1</establishment_count>
</city>
<city>
<city_id>7920</city_id>
<country>AUSTRIA</country>
<name>Traiskirchen</name>
<establishment_count>1</establishment_count>
</city>
</cities>
and the php code like this:
<?php
$source = file_get_contents('cities.xml');
$xml = new SimpleXMLElement($source);
foreach ($xml as $node)
{
$row = simplexml_load_string($node->asXML());
$result = $row->xpath("//city/name");
if ($result[0])
{
$name = $row->name;
echo "<div>".$name.", ".$row->country."</div>";
}
}
?>
the code is doing fine and printing the result like this:
Petrosino, ITALY
Traiskirchen, AUSTRIA
here i dont know how to print the data if its matching the string pattern. Just like if i pass the string "lon" so its display only those city name which are having "lon" string pattern like "london"
please help me with this
Use contains():
$string = '<?xml version="1.0" encoding="utf-8"?>
<cities>
<city>
<city_id>8393</city_id>
<country>ITALY</country>
<name>Petrosino</name>
<establishment_count>1</establishment_count>
</city>
<city>
<city_id>7920</city_id>
<country>AUSTRIA</country>
<name>Traiskirchen</name>
<establishment_count>1</establishment_count>
</city>
</cities>';
$xml = new SimpleXMLElement($string);
$result = $xml->xpath("//city/name[contains(., 'Pet')]");
print_r($result);
/*Array
(
[0] => SimpleXMLElement Object
(
[0] => Petrosino
)
)*/
or for your problem:
$string = 'Pet';
foreach ($xml as $node){
$row = simplexml_load_string($node->asXML());
$result = $row->xpath("name[contains(., '".$string."')]");
if ($result[0]){
$name = $row->name;
echo "<div>".$name.", ".$row->country."</div>";
}
}
Codepad Example
for case insensitive it's somewhat ugly:
$string = 'pet';
$result = $xml->xpath("//city[contains(translate(name,'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), '".strtoupper($string)."')]");
foreach($result as $one){
echo "<div>".$one->name.", ".$one->country."</div>";
}
You want to use preg_match. E.g.
$pattern = '/^lon/';
if (preg_match($pattern, $name)){
// do your print out
}
More info here: http://php.net/manual/en/function.preg-match.php

Categories