Php loop throught Xml nodes - php

I have Xml file like
<response>
<tag1>
<item>
<id>106</id>
<title>DG</title>
</item>
<item>
<id>105</id>
<title>AC</title>
</item>
</tag1>
<tag2>
<item>
<id>1</id>
<title>DjG</title>
</item>
<item>
<id>15</id>
<title>AoC</title>
</item>
</tag2>
</response>
I trying this code to extract ID and Title
$dom = new DomDocument();
$dom->load('xml.xml');
$xpath = new DOMXPath($dom);
foreach($xpath->evaluate('//response/*') as $node){
$params =$xpath->evaluate('//response/' .$node->nodeName . '/item/*');
foreach($params as $child) {
echo $node->nodeName ." = " .$child->nodeName ." = " .$child->nodeValue ."\n<br>";
}
}
But I get result such
<br>tag1 = id = 106
<br>tag1 = title = DG
<br>tag1 = id = 105
<br>tag1 = title = AC
<br>tag2 = id = 1
<br>tag2 = title = DjG
<br>tag2 = id = 15
<br>tag2 = title = AoC
But i need to get like this
tag1 = 106 = DG
tag1 = 105 = AC
tag2 = 1 = DjG
tag2 = 15 = AoC

On the second foreach, just target that $node->nodeName, then on the inner foreach target each id an title.
foreach($xpath->evaluate('//response/*') as $node) {
$tag = $node->nodeName;
$params = $xpath->evaluate("//$tag/*");
foreach($params as $child) {
$id = $xpath->evaluate('string(./id)', $child);
$title = $xpath->evaluate('string(./title)', $child);
echo $tag ." = " .$id ." = " .$title ."\n<br>";
}
}
Sample Output
Or the SimpleXML version:
$xml = simplexml_load_file('xml.xml');
foreach($xml as $tag =>$node) {
foreach($node as $item => $child) {
echo $tag ." = " .$child->id ." = " .$child->title ."\n<br>";
}
}
Sample Output

Use this code:
$dom = new DomDocument();
$dom->load('xml.xml');
$xpath = new DOMXPath($dom);
foreach($xpath->evaluate('//response/*') as $node){
$params =$xpath->evaluate('//response/' .$node->nodeName . '/*');
foreach($params as $child)
{
echo $node->nodeName ." = " .$child->getElementsByTagName('id')->item(0)->textContent ." = " .$child->getElementsByTagName('title')->item(0)->textContent ."\n<br>";
}
}

The second argument of DOMXpath::evaluate() is the context node. If you do not start the XPath expression with a slash, it will be relative to it. So inside a loop, you usually want to use the current node as the context for your expressions.
foreach ($xpath->evaluate('/absolute-expression') as $node) {
var_dump(
$xpath->evaluate('relative-expression', $node)
);
}
XPath (unlike CSS selectors) can fetch properties of the elements along different axes. You can get the local name (without namespace prefix) of a parent node with:
local-name(parent::*)
Using this, you can solve the problem with a single loop iterating the item elements.
Example:
$dom = new DomDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
foreach ($xpath->evaluate('//response/*/item') as $node) {
echo
$xpath->evaluate('local-name(parent::*)', $node), ' = ',
$xpath->evaluate('string(id)', $node), ' = ',
$xpath->evaluate('string(title)', $node), "\n";
}
Output:
tag1 = 106 = DG
tag1 = 105 = AC
tag2 = 1 = DjG
tag2 = 15 = AoC
Only, If you need to call source for each first level node (tag1, tag2). You will need two loops. Like output the items grouped by the tag* element node.
$dom = new DomDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
foreach ($xpath->evaluate('//response/*') as $node) {
echo
$xpath->evaluate('local-name()', $node),
"\n--------\n";
foreach ($xpath->evaluate('item', $node) as $item) {
echo
$xpath->evaluate('string(id)', $item), ' = ',
$xpath->evaluate('string(title)', $item), "\n";
}
echo "\n";
}
Output:
tag1
--------
106 = DG
105 = AC
tag2
--------
1 = DjG
15 = AoC

Related

Delete Node isn't working with Simple XML (PHP)

I want to delete a node if the title of an node is matching a filter (array). I use unset() and I already tried $node and $item but both arguments won't delete my node...
What is wrong in this code? - I do enter the if condition, because I see in if in my console!
$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load("shop1.xml");
$pathXML = "/products/product";
$titleArray = array("Test", "Battlefield 1");
$doc = simplexml_import_dom($dom);
$items = $doc->xpath($pathXML);
foreach ($items as $item) {
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
echo $title . "\n";
foreach ($titleArray as $titles) {
echo $titles . "\n";
if (mb_stripos($title, $titles) !== false) {
echo "in if\n\n";
unset($item);
}
}
}
$dom->saveXML();
$dom->save("shop1_2.xml");
XML File:
<products>
<product>
<title>Battlefield 1</title>
<url>https://www.google.de/</url>
<price>0.80</price>
</product>
<product>
<title>Battlefield 2</title>
<url>https://www.google.de/</url>
<price>180</price>
</product>
</products>
Greetings and Thank You!
All you're doing is unsetting a local variable. Instead you need to alter the DOM:
$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load("shop1.xml");
$xpathQuery = "/products/product";
$titleArray = array("Test", "Battlefield 1");
$xp = new DomXpath($dom);
$items = $xp->query($xpathQuery);
foreach ($items as $item) {
$title = $item->getElementsByTagName('title')->item(0)->textContent;
echo "$title\n";
if (in_array($title, $titleArray)) {
$item->parentNode->removeChild($item);
}
}
$dom->saveXML();
$dom->save("shop1_2.xml");

PHP DOM Document - get everything between two nodes

I have this as a part of my XML that I am loading in a DOM Document:
<error n='\Author'/>
Some Text 1
<formula type='inline'><math xmlns='http://www.w3.org/1998/Math/MathML'><msup><mrow/> <mrow><mn>1</mn><mo>,</mo></mrow> </msup></math></formula>
Some Text 2
<formula type='inline'><math xmlns='http://www.w3.org/1998/Math/MathML'><msup><mrow/> <mn>2</mn> </msup></math></formula>
<error n='\address' />
My goal is to get everything as nodeValue between the
<error n='\Author' />
And
<error n='\address' />
How can this be done?
I tested this:
$author_node = $xpath_xml->query("//error[#n='\Author']/following-sibling::*[1]")->item(0);
if ($author_node != null) {
$i = 1;
$nextNodeName = "";
$author = "";
while ($nextNodeName != "error" && $i < 20) {
$nextNodeName = $xpath_xml->query("//error[#n='\Author']/following-sibling::*[$i]")->item(0)->tagName;
if ($nextNodeName == "error")
continue;
$author .= $nextNode->nodeValue;
}
But Am getting only the formula content, not the text between formulas.
Thank you.
The *only selects element nodes, not text nodes. So only the <formula> elements are selected. You need to use node(). But you could use xpath directly to selected the needed nodes. Look for an explanation of the Kayessian method.
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
$nodes = $xpath->evaluate(
'//error[#n="\\Author"][1]
/following-sibling::node()
[
count(
.|
//error[#n="\\Author"][1]
/following-sibling::error[#n="\\address"][1]
/preceding-sibling::node()
)
=
count(
//error[#n="\\Author"][1]
/following-sibling::error[#n="\\address"][1]
/preceding-sibling::node()
)
]'
);
$result = '';
foreach ($nodes as $node) {
$result .= $node->nodeValue;
}
var_dump($result);
Demo: https://eval.in/125494
If you want to save not only the text content, but the XML fragment, you can use DOMDocument::saveXml() with the node as argument.
$result = '';
foreach ($nodes as $node) {
$result .= $node->ownerDocument->saveXml($node);
}
var_dump($result);

XML Obtain multiple values

I have the below XML file. There are 4 rows constantly repeated for different websites.
These are _URL _Away _Home _Draw. Each of these is prepended by the website. I need to compare all of the _Away rows to find the highest value, but there may sometimes be 1 of these rows and other times there can be as many as 32. What I would like to know is, is there a way to get these by defining the end of the string without having to explicitly declare the entire string for each website?
<XMLSOCCER.COM>
<Odds>
<Id>1547</Id>
<_10Bet_Home_Home>1.31</_10Bet_Home_Home>
<_10Bet_Home_Url>http://en.10bet.com</_10Bet_Home_Url>
<_10Bet_Home_Away>8.50</_10Bet_Home_Away>
<_10Bet_Home_Draw>5.40</_10Bet_Home_Draw>
<Bet_At_Home_Home>1.25</Bet_At_Home_Home>
<Bet_At_Home_Url>http://www.bet-at-home.com/</Bet_At_Home_Url>
<Bet_At_Home_Away>10.00</Bet_At_Home_Away>
<Bet_At_Home_Draw>5.75</Bet_At_Home_Draw>
<Bet365_Url>http://www.bet365.com/</Bet365_Url>
<Bet365_Home>1.30</Bet365_Home>
<Bet365_Away>9.00</Bet365_Away>
<Bet365_Draw>5.50</Bet365_Draw>
<BetVictor_Home>1.30</BetVictor_Home>
<BetVictor_Url>http://www.betvictor.com/</BetVictor_Url>
<BetVictor_Away>9.00</BetVictor_Away>
<BetVictor_Draw>5.40</BetVictor_Draw>
<Bwin_Home>1.28</Bwin_Home>
</Odds>
</XMLSOCCER.COM>
You can use XPath to fetch all nodes ending with _Away. Here's a code snippet that accomplishes what you want:
<?php
$xml = <<<XML
<XMLSOCCER.COM>
<Odds>
<Id>1547</Id>
<_10Bet_Home_Home>1.31</_10Bet_Home_Home>
<_10Bet_Home_Url>http://en.10bet.com</_10Bet_Home_Url>
<_10Bet_Home_Away>8.50</_10Bet_Home_Away>
<_10Bet_Home_Draw>5.40</_10Bet_Home_Draw>
<Bet_At_Home_Home>1.25</Bet_At_Home_Home>
<Bet_At_Home_Url>http://www.bet-at-home.com/</Bet_At_Home_Url>
<Bet_At_Home_Away>10.00</Bet_At_Home_Away>
<Bet_At_Home_Draw>5.75</Bet_At_Home_Draw>
<Bet365_Url>http://www.bet365.com/</Bet365_Url>
<Bet365_Home>1.30</Bet365_Home>
<Bet365_Away>9.00</Bet365_Away>
<Bet365_Draw>5.50</Bet365_Draw>
<BetVictor_Home>1.30</BetVictor_Home>
<BetVictor_Url>http://www.betvictor.com/</BetVictor_Url>
<BetVictor_Away>9.00</BetVictor_Away>
<BetVictor_Draw>5.40</BetVictor_Draw>
<Bwin_Home>1.28</Bwin_Home>
</Odds>
</XMLSOCCER.COM>
XML;
$sxe = new SimpleXMLElement($xml);
$nodesEndingWithAway = $sxe->xpath('//*[substring(name(),string-length(name())-3) = "Away"]');
$highestValue = 0;
$nodeName = '';
foreach ($nodesEndingWithAway as $node) {
if ((float) $node > $highestValue) {
$highestValue = (float) $node;
$nodeName = $node->getName();
}
}
echo "Highest value is {$highestValue} from node {$nodeName}.\n";
Output:
Highest value is 10 from node Bet_At_Home_Away.
Note: I think it would be possible to accomplish it with a single XPath expression without the need to process the nodes with the foreach.
You can do this with XPath.
$doc = new DOMDocument();
$doc->load($filename);
$xpath = new DOMXPath($doc);
$elements = $xpath->query('/XMLSOCCER.COM/Odds/*[substring(name(),string-length(name())-3) = "Away"]');
$maxValue = 0;
foreach ($elements as $element) {
$value = floatval($element->nodeValue);
$maxValue = max($maxValue, $value);
}
EDIT: very compressed:
$maxbid = max(array_map('floatval', $xml->xpath("//*[substring(name(),string-length(name())-" . (strlen($search) - 1) . ") = '$search']")));
in several steps:
use simplexml and xpath:
$search = "_Away";
$xml = simplexml_load_string($x);
$results = $xml->xpath("//*[substring(name(),string-length(name())-" . (strlen($search) - 1) . ") = '$search']");
Loop through your results:
foreach ($results as $result) echo "$result <br />";
Print highest result:
echo "highest: " . number_format(max(array_map('floatval', $results)), 2, '.', ',');
See it working: http://codepad.viper-7.com/iEpGz9

Get specific text from webpage

I have this Page Test1 on this other page test
I have this PHP code running to get some code from test1.
<?php
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTMLFile("http://inviatapenet.gethost.ro/sop/test1.php");
$xpath = new DOMXpath($doc);
$elements = $xpath->query("//*[#type='button']/#onclick");
if (!is_null($elements)) {
foreach ($elements as $element) {
$nodes = $element->childNodes;
foreach ($nodes as $node) {
echo $node->nodeValue. "\n";
}
}
}
?>
The result is this
OnPlay('sop://broker.sopcast.com:3912/120704 cod ', ' eu - Nr.1 in tv ! ')
OnPlay('sop://broker.sopcast.com:3912/140601 cod ', ' eu - Nr.1 in tv ! ')
OnPlay('sop://broker.sopcast.com:3912/124589 cod ', ' eu - Nr.1 tv')
OnPlay('sop://broker.sopcast.com:3912/589994 cod ', ' eu - tv ')
OnPlay('sop://broker.sopcast.com:3912/ cod ', ' eu - tv ')
But I need only this data from all of that: `sop://broker.sopcast.com:3912/140601
All of them.
How to get rid of extra text or how to get gest the(sop://broker.sopcast.com:3912/140601,sop://broker.sopcast.com:3912/120704)
If the string is always formatted like this, you can simply use explode to get the sop:// URL.
<?php
header('Content-Type: text/plain; charset=UTF-8');
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTMLFile("http://inviatapenet.gethost.ro/sop/test1.php");
$xpath = new DOMXpath($doc);
$elements = $xpath->query("//*[#type='button']/#onclick");
if (!is_null($elements)) {
foreach ($elements as $element) {
$nodes = $element->childNodes;
foreach ($nodes as $node) {
echo $node->nodeValue. "\n";
$content = $node->nodeValue;
$content = explode("'", $content, 3);
$content = explode(" ", $content[1], 2);
$sop = $content[0];
unset($content);
var_dump($sop);
}
}
}
?>
I think you might need do some string manipulation on resultant OnClick event handlers text.
<?php
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTMLFile("http://inviatapenet.gethost.ro/sop/test1.php");
$xpath = new DOMXpath($doc);
$elements = $xpath->query("//*[#type='button']/#onclick");
$value_text = array();
$index = 0;
if (!is_null($elements)) {
foreach ($elements as $element) {
$nodes = $element->childNodes;
foreach ($nodes as $node) {
value_text[$index++] = getReuiredValue($node->nodeValue);
}
}
//value_text will contain all required values as array
print_r($value_text);
}
function getReuiredValue($on_play)
{
$pos = strpos($on_play, 'cod ');
//following call will parse the OnPlay string and get the required value out of string
$updated_on_play = substr($on_play, 8, (strlen($on_play) - (strlen($on_play) - $pos) - 8));
$updated_on_play = trim($updated_on_play);
return $updated_on_play;
}
?>

Need to show child data on parent id

i'm struggling with Xpath, i have an xml list and i need to get the child data based on the parent id ...
My xml file :
<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
the code i use to get for example the project 1 data (does not work):
$xmlDoc = new DOMDocument();
$xmlDoc->load(data.xml);
$xpath = new DOMXPath($xmlDoc);
$projectId = '1';
$query = '//projecten/project[#id='.$projectId.']';
$details = $xpath->query($query);
foreach( $details as $detail )
{
echo $detail->titel;
echo $detail->siteurl;
echo $detail->screenshot;
echo $detail->omschrijving;
}
But this does not show anything, if someone can point me out ... thanks
In addition to the solution already given you can also use:
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$projectNode->getElementsByTagName('titel')->item(0)->nodeValue,
$projectNode->getElementsByTagName('siteurl')->item(0)->nodeValue,
$projectNode->getElementsByTagName('screenshot')->item(0)->nodeValue,
$projectNode->getElementsByTagName('omschrijving')->item(0)->nodeValue;
}
or fetch the DOMText node values directly with Xpath
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$xpath->evaluate('string(titel)', $projectNode),
$xpath->evaluate('string(siteurl)', $projectNode),
$xpath->evaluate('string(screenshot)', $projectNode),
$xpath->evaluate('string(omschrijving)', $projectNode);
}
or import the node to SimpleXml
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
$detail = simplexml_import_dom($projectNode);
echo
$detail->titel,
$detail->siteurl,
$detail->screenshot,
$detail->omschrijving;
}
or even concatenate all the values directly in the XPath:
$xpath = new DOMXPath($dom);
echo $xpath->evaluate(
sprintf(
'concat(
/projecten/project[#id = %1$d]/titel,
/projecten/project[#id = %1$d]/siteurl,
/projecten/project[#id = %1$d]/screenshot,
/projecten/project[#id = %1$d]/omschrijving
', $id
)
);
Accessing the child nodes as you do:
echo $detail->title;
Is not valid, if you use DOM* functions. This would probably work if you were using SimpleXML.
For DOM* try this:
$dom = new DOMDocument;
$dom->loadXml('<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
');
$id = 2;
$xpath = new DOMXPath($dom);
foreach ($xpath->query(sprintf('/projecten/project[#id="%s"]', $id)) as $projectNode) {
// repeat this for every needed node
$titleNode = $xpath->query('titel', $projectNode)->item(0);
if ($titleNode instanceof DOMElement) {
echo $titleNode->nodeValue;
}
// or us a loop for all child nodes
foreach ($projectNode->childNodes as $childNode) {
echo $childNode->nodeValue;
}
}

Categories