I'm trying to use xpath in conjunction with DOMDocument to try and parse my xml and insert into a table.
All my variables are inserting correctly other than $halftimescore - why is this?
Here is my code:
<?php
define('INCLUDE_CHECK',true);
require 'db.class.php';
$dom = new DOMDocument();
$dom ->load('main.xml');
$xpath = new DOMXPath($dom);
$queryResult = $xpath->query('//live/Match/Results/Result[#name="HT"]');
foreach($queryResult as $resulty) {
$halftimescore=$resulty->getAttribute("value");
}
$Match = $dom->getElementsByTagName("Match");
foreach ($Match as $match) {
$matchid = $match->getAttribute("id");
$home = $match->getElementsByTagName("Home");
$hometeam = $home->item(0)->getAttribute("name");
$homeid = $home->item(0)->getAttribute("id");
$away = $match->getElementsByTagName("Away");
$awayid = $away->item(0)->getAttribute("id");
$awayteam = $away->item(0)->getAttribute("name");
$leaguename = $match->getElementsByTagName("league");
$league = $leaguename->item(0)->nodeValue;
$leagueid = $leaguename->item(0)->getAttribute("id");
foreach ($match->getElementsByTagName('Result') as $result) {
$resulttype = $result->getAttribute("name");
$score = $result->getAttribute("value");
$scoreid = $result->getAttribute("value");
}
mysql_query("
INSERT INTO blabla
(home_team, match_id, ht_score, away_team)
VALUES
('".$hometeam."', '".$matchid."', '".$halftimescore."', '".$awayteam."')
");
}
Because you populated $halftimescore outside the main loop, in a loop of its own, it will only have one value (the last value) because each iteration overwrites the previous.
What you need to do instead is run the XPath query within the main loop, with a base node of the current node, like this:
// ...
$xpath = new DOMXPath($dom);
/*
Remove these lines from here...
$queryResult = $xpath->query('//live/Match/Results/Result[#name="HT"]');
foreach($queryResult as $resulty) {
$halftimescore=$resulty->getAttribute("value");
}
*/
$Match = $dom->getElementsByTagName("Match");
foreach ($Match as $match) {
// and do the query here instead:
$result = $xpath->query('./Results/Result[#name="HT"]', $match);
if ($result->length < 1) {
// handle this error - the node was not found
}
$halftimescore = $result->item(0)->getAttribute("value");
// ...
Related
I need to extract information from an XML using XMLDom.
Below is myroot.xml
<?xml version='1.0' encoding='ISO-8859-1'?>
<myroot xml:lang='en'>
<delta>
<history>
<detail>
<id>one</id>
<degree>
<dname>alpha</dname>
<dates>
<StartDate>
<Year>1998</Year>
</StartDate>
<EndDate>
<Year>2002</Year>
</EndDate>
</dates>
</degree>
</detail>
<detail>
<id>two</id>
<degree>
<dname>beta</dname>
<dates>
<StartDate>
<Year>2006</Year>
</StartDate>
<EndDate>
<Year>2008</Year>
</EndDate>
</dates>
</degree>
</detail>
</history>
</delta>
here is my code
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$rootxmldoc = $doc->load('myroot.xml');
$xpath = new DOMXPath($rootxmldoc);
$items = $hrxml_obj->getElementsByTagName("detail");
$subitemarray = array();
$icounter = 0;
foreach ($items as $item) {
$query = "//dates/*/Year"; //xpath of all occurrence of Year
$entries = $xpath->query($query, $item);
foreach ($entries as $entry) {
$dates["startdate"] = "todo"; //extract StartDate
$dates["enddate"] = "todo"; //extract EndDate
}
$subitemarray[$icounter++] = dates;
}
var_dump($subitemarray);
Ideally I need to extract dates using xpath. I am not able to get this nailed. any help is appreciated. The issue is the usage of xpath while looping.
With XPath go directly to yout dates tag, and then use DOMElement::getElementsByTagName() to get StartDate and EndDate (you can also go to the dates tag using DOMDocument::getElementsByTagName(), but XPath gives you more flexibility should you need it). This will return a DOMNodeList, but you know (if the structure is constant) that you only need the first element of the list. So:
// $xml ommited, saved in a variable for testing purposes
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$items = $doc->getElementsByTagName("detail");
$subitemarray = array();
$icounter = 0;
foreach ($items as $item) {
$query = "//dates"; //xpath of all occurrence of Year
$entries = $xpath->query($query, $item);
foreach ($entries as $entry) {
$startDate = $entry->getElementsByTagName("StartDate")[0]->nodeValue;
$endDate = $entry->getElementsByTagName("EndDate")[0]->nodeValue;
$dates["startdate"] = $startDate; //extract StartDate
$dates["enddate"] = $endDate; //extract EndDate
}
$subitemarray[$icounter++] = $dates;
}
var_dump($subitemarray);
Demo
Or only with XPath:
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$items = $doc->getElementsByTagName("detail");
$subitemarray = array();
$icounter = 0;
foreach ($items as $item) {
$queryStart = "//dates/StartDate";
$entriesStart = $xpath->query($queryStart, $item);
$dates["startdate"] = $entriesStart[0]->nodeValue;
$queryEnd = "//dates/EndDate";
$entriesEnd = $xpath->query($queryEnd, $item);
$dates["enddate"] = $entriesEnd[0]->nodeValue;
$subitemarray[$icounter++] = $dates;
}
var_dump($subitemarray);
And lastly, using only one XPath query:
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$items = $doc->getElementsByTagName("detail");
$subitemarray = array();
$icounter = 0;
foreach ($items as $item) {
$query = "//dates/*[contains(local-name(), 'Date')]
";
$entries = $xpath->query($query, $item);
$dates["startdate"] = $entries[0]->nodeValue;
$dates["enddate"] = $entries[1]->nodeValue;
$subitemarray[$icounter++] = $dates;
}
var_dump($subitemarray);
Demo
The query will simply get any elements inside the current detail element that contains the word "Date". Again, if the structure is constant, you can assume that the first result will be StartDate and the second result will be EndDate.
I would like to be able to create an XML file from some of the content of a html page. I have tried intensively but seem to miss something.
I have created two arrays, I have setup a DOMdocument and I have prepared to save an XML file on the server... I have tried to make tons of different foreach loops all over the place - but it won't work.
Here is my code:
<?php
$page = file_get_contents('http://www.halfmen.dk/!hmhb8/score.php');
$doc = new DOMDocument();
$doc->loadHTML($page);
$score = $doc->getElementsByTagName('div');
$keyarray = array();
$teamarray = array();
foreach ($score as $value) {
if ($value->getAttribute('class') == 'xml') {
$keyarray[] = $value->firstChild->nodeValue;
$teamarray[] = $value->firstChild->nextSibling->nodeValue;
}
}
print_r($keyarray);
print_r($teamarray);
$doc = new DOMDocument('1.0','utf-8');
$doc->formatOutput = true;
$droot = $doc->createElement('ROOT');
$droot = $doc->appendChild($droot);
$dsection = $doc->createElement('SECTION');
$dsection = $droot->appendChild($dsection);
$dkey = $doc->createElement('KEY');
$dkey = $dsection->appendChild($dkey);
$dteam = $doc->createElement('TEAM');
$dteam = $dsection->appendChild($dteam);
$dkeytext = $doc->createTextNode($keyarray);
$dkeytext = $dkey->appendChild($dkeytext);
$dteamtext = $doc->createTextNode($teamarray);
$dteamtext = $dteam->appendChild($dteamtext);
echo $doc->save('xml/test.xml');
?>
I really like simplicity, thank you.
You need to add each item in one at a time rather than as an array, which is why I build the XML for each div tag rather than as a second pass. I've had to assume that your XML is structured the way I've done it, but this may help you.
$page = file_get_contents('http://www.halfmen.dk/!hmhb8/score.php');
$doc = new DOMDocument();
$doc->loadHTML($page);
$score = $doc->getElementsByTagName('div');
$doc = new DOMDocument('1.0','utf-8');
$doc->formatOutput = true;
$droot = $doc->createElement('ROOT');
$droot = $doc->appendChild($droot);
foreach ($score as $value) {
if ($value->getAttribute('class') == 'xml') {
$dsection = $doc->createElement('SECTION');
$dsection = $droot->appendChild($dsection);
$dkey = $doc->createElement('KEY', $value->firstChild->nodeValue);
$dkey = $dsection->appendChild($dkey);
$dteam = $doc->createElement('TEAM', $value->firstChild->nextSibling->nodeValue);
$dteam = $dsection->appendChild($dteam);
}
}
Here is my code:
$res = file_get_contents("http://www.lenzor.com/photo/search/index/type/user/%D8%B9%D9%84%DB%8C//text/%D9%81%D8%A7%D8%B7%D9%85%D9%87");
$doc = new \DOMDocument();
#$doc->loadHTMLFile($res);
$xpath = new \DOMXpath($doc);
$links = $xpath->query("//ul[#class='user_box']/li");
$result = array();
if (!is_null($links)) {
foreach ($links as $link) {
$href = $link->getAttribute('class');
$result[] = [$href];
}
}
print_r($result);
Here is the content I'm working on. I mean it's the result of echo $res.
Ok well, the result of my code is an empty array. So $links is empty and that foreach won't be executed. Why? Why //ul[#class='user_box']/li query doesn't match the DOM ?
Expected result is an array contains the class attribute of lis.
Try this, Hope this will be helpful. There are few mistakes in your code.
1. You should search like this '//ul[#class="user_box clearfix"]/li' because class="user_box clearfix" class attribute of that HTML source contains two classes.
2. You should use loadHTMLinstead of loadHTMLFile.
<?php
ini_set('display_errors', 1);
libxml_use_internal_errors(true);
$res = file_get_contents("http://www.lenzor.com/photo/search/index/type/user/%D8%B9%D9%84%DB%8C//text/%D9%81%D8%A7%D8%B7%D9%85%D9%87");
$doc = new \DOMDocument();
$doc->loadHTML($res);
$xpath = new \DOMXpath($doc);
$links = $xpath->query('//ul[#class="user_box clearfix"]/li');
$result = array();
if (!is_null($links)) {
foreach ($links as $link) {
$href = $link->getAttribute('class');
$result[] = [$href];
}
}
print_r($result);
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
How do I rename a value in xml using PHP? This is what I've got so far:
<?php
$q = $_GET["q"];
$q = stripslashes($q);
$q = explode('|^', $q);
$old = $q[0];
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->Load("test.xml");
$xpath = new DOMXPath($dom);
$query1 = 'channel/item[title="' . $old . '"]/title';
$entries = $xpath->query($query1);
foreach ($entries as $entry)
{
$oldchapter = $entry->parentNode->removeChild($entry);
$item = $dom->getElementsByTagName('item');
foreach ($item as $items)
{
$title = $dom->createElement('title', $q[1]);
$items->appendChild($title);
}
}
$dom->save("test.xml");
Basically, what it does is take two titles from a url, the old existing title, and the one the user wants to change it to (so like this oldtitle|^newtitle), and puts them into an array.
What I've tried doing is removing the existing old title, and then making a new title with, using the new title value from the url, but it doesn't seem to be working. Where am I going wrong, or is there an easier way of doing this?
The way to do this is with DOMNode::replaceChild(). The majority of your code is correct, you've just slightly over-complicated some of the DOM stuff.
Try this:
<?php
$q = $_GET["q"];
$q = stripslashes($q);
$q = explode('|^', $q);
$old = $q[0];
$dom = new DOMDocument;
// Do this *before* loading the document
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->Load("test.xml");
$xpath = new DOMXPath($dom);
$query1 = 'channel/item[title="' . $old . '"]/title';
$entries = $xpath->query($query1);
// This is all you need to do in the loop
foreach ($entries as $oldTitle) {
$newTitle = $dom->createElement('title', $q[1]);
$entry->parentNode->replaceChild($newTitle, $oldTitle);
}
$dom->save("test.xml");