parsing a DOMNodeList in PHP - php

A web service return Xml of format
<string>
<NewDataSet>
<DealBlotter>
<CustomerReference>161403239</CustomerReference>
<Symbol>EUR/USD</Symbol>
<BuySell>S</BuySell>
<ContractValue>-100000</ContractValue>
<Price>1.35070</Price>
<CounterValue>-135070</CounterValue>
<TradeDate>2011-01-20 22:05:21.690</TradeDate>
<ConfirmationNumber>78967117</ConfirmationNumber>
<Status>C</Status>
<lTID>111913820</lTID>
</DealBlotter>
</NewDataSet>
</string>
Now i am using curl to access this and then -
$xml = simplexml_load_string($result);
$dom = new DOMDOcument();
// Load your XML as a string
$dom->loadXML($xml);
// Create new XPath object
$xpath = new DOMXpath($dom);
$res = $xpath->query("/NewDataSet/DealBlotter");
foreach($res as $node)
{
print "i went inside foreach";
$custref = ($node->getElementsByTagName("CustomerReference")->item(0)->nodeValue);
print $custref;
$ccy = ($node->getElementsByTagName("Symbol")->item(0)->nodeValue);
print $ccy;
$type = ($node->getElementsByTagName("BuySell")->item(0)->nodeValue);
$lots = ($node->getElementsByTagName("ContractValue")->item(0)->nodeValue);
$price = ($node->getElementsByTagName("Price")->item(0)->nodeValue);
$confnumber = ($node->getElementsByTagName("ConfirmationNumber")->item(0)->nodeValue);
$status = ($node->getElementsByTagName("Status")->item(0)->nodeValue);
$ltid = ($node->getElementsByTagName("lTID")->item(0)->nodeValue);
$time = ($node->getElementsByTagName("TradeDate")->item(0)->nodeValue);
}
But nothing is getting printed. except the dummy statement.
using $res = $xpath->query("/string/NewDataSet/DealBlotter"); did not help. Also a print_r($res); gives output as DOMNodeList obect.
Doing this also does not print anything
$objDOM = new DOMDocument();
$objDOM->load($result);
$note = $objDOM->getElementsByTagName("DealBlotter");
foreach( $note as $value )
{
print "hello";
$tasks = $value->getElementsByTagName("Symbol");
$task = (string)$tasks->item(0)->nodeValue;
$details = $value->getElementsByTagName("Status");
$detail = (string)$details->item(0)->nodeValue;
print "$task :: $detail <br>";
}

There are a few problems.
With how you're loading the xml. Get rid of the simplexml line. It's not needed, and is messing things up. Instead just do $dom->loadXml($result);. There's no reason to load SimpleXML first if you're going to pass it directly into DomDocument.
With your query, the / operator is the direct decendent operator. So it means directly next to. So your first tag should be the root. So either add the root onto it:
$res = $xpath->query("/string/NewDataSet/DealBlotter");
Or make the leading slash into // which selects any matching decendent:
$res = $xpath->query("//NewDataSet/DealBlotter");
And finally, doing a var_dump on $res isn't going to tell you much. Instead, I like to do var_dump($res->length) since it'll tell you how many matches it has rather than that it's a domnodelist (which you already know)...

Related

DOMDocument not updating nodeValue

So I wrote a piece of code which is supposed to be editing the xml file. But it doesn't seem to work. I have checked everything and all data seems to come through, but somehow it does not update the nodes. Creating the xml file and the data works, adding data works too. But somehow I can't seem to update it.
if ($edit && isset($_POST["submit"])) {
$doc = new DomDocument('1.0');
$doc->validateOnParse = true;
$doc->load('data.xml');
$message = getElementById($_GET["id"], $doc);
$message->getElementsByTagName("title")->nodeValue = 'hey';
$message->getElementsByTagName("content")->nodeValue = $_POST["content"];
$target = $message->getElementsByTagName("target")->nodeValue = $_POST["target"];
$date1 = $message->getElementsByTagName("startDate")->nodeValue = $_POST["date1"];
$date2 = $message->getElementsByTagName("endDate")->nodeValue = $_POST["date2"];
$doc->formatOutput = true;
$doc->save('data.xml');
}
function getElementById($id, $doc)
{
$xpath = new DOMXPath($doc);
return $xpath->query("//*[#id='$id']")->item(0);
}
XML:
<message id="5a1c301ae5429" top="12px" left="12px" duration="20">
<title>hey</title>
<content>12345</content>
<target>2</target>
<startDate>27/11/2017 16:30</startDate>
<endDate>27/11/2017 16:50</endDate>
<media type="image" width="200px" height="200px" top="-20px" left="129px">
<uri>
localhost/xml/uploads/4215c27edf5ff51aee0def29f84949be.jpg
</uri>
</media>
</message>
When you call getElementsByTagName, this returns a list of nodes that match the tag name. So each time you access the value, you should use...
$message->getElementsByTagName("title")->item(0)->nodeValue = 'hey';
As you only have 1 of each tag, I've used ->item(0) to fetch the first node in the list.
Repeat the same logic for all times you need to access the elements.

domDocument's formatOutput property writes inline [duplicate]

Here are the codes:
$doc = new DomDocument('1.0');
// create root node
$root = $doc->createElement('root');
$root = $doc->appendChild($root);
$signed_values = array('a' => 'eee', 'b' => 'sd', 'c' => 'df');
// process one row at a time
foreach ($signed_values as $key => $val) {
// add node for each row
$occ = $doc->createElement('error');
$occ = $root->appendChild($occ);
// add a child node for each field
foreach ($signed_values as $fieldname => $fieldvalue) {
$child = $doc->createElement($fieldname);
$child = $occ->appendChild($child);
$value = $doc->createTextNode($fieldvalue);
$value = $child->appendChild($value);
}
}
// get completed xml document
$xml_string = $doc->saveXML() ;
echo $xml_string;
If I print it in the browser I don't get nice XML structure like
<xml> \n tab <child> etc.
I just get
<xml><child>ee</child></xml>
And I want to be utf-8
How is this all possible to do?
You can try to do this:
...
// get completed xml document
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$xml_string = $doc->saveXML();
echo $xml_string;
You can make set these parameter right after you've created the DOMDocument as well:
$doc = new DomDocument('1.0');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
That's probably more concise. Output in both cases is (Demo):
<?xml version="1.0"?>
<root>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
</root>
I'm not aware how to change the indentation character(s) with DOMDocument. You could post-process the XML with a line-by-line regular-expression based replacing (e.g. with preg_replace):
$xml_string = preg_replace('/(?:^|\G) /um', "\t", $xml_string);
Alternatively, there is the tidy extension with tidy_repair_string which can pretty print XML data as well. It's possible to specify indentation levels with it, however tidy will never output tabs.
tidy_repair_string($xml_string, ['input-xml'=> 1, 'indent' => 1, 'wrap' => 0]);
With a SimpleXml object, you can simply
$domxml = new DOMDocument('1.0');
$domxml->preserveWhiteSpace = false;
$domxml->formatOutput = true;
/* #var $xml SimpleXMLElement */
$domxml->loadXML($xml->asXML());
$domxml->save($newfile);
$xml is your simplexml object
So then you simpleXml can be saved as a new file specified by $newfile
<?php
$xml = $argv[1];
$dom = new DOMDocument();
// Initial block (must before load xml string)
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// End initial block
$dom->loadXML($xml);
$out = $dom->saveXML();
print_R($out);
Tried all the answers but none worked. Maybe it's because I'm appending and removing childs before saving the XML.
After a lot of googling found this comment in the php documentation. I only had to reload the resulting XML to make it work.
$outXML = $xml->saveXML();
$xml = new DOMDocument();
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
$xml->loadXML($outXML);
$outXML = $xml->saveXML();
// ##### IN SUMMARY #####
$xmlFilepath = 'test.xml';
echoFormattedXML($xmlFilepath);
/*
* echo xml in source format
*/
function echoFormattedXML($xmlFilepath) {
header('Content-Type: text/xml'); // to show source, not execute the xml
echo formatXML($xmlFilepath); // format the xml to make it readable
} // echoFormattedXML
/*
* format xml so it can be easily read but will use more disk space
*/
function formatXML($xmlFilepath) {
$loadxml = simplexml_load_file($xmlFilepath);
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($loadxml->asXML());
$formatxml = new SimpleXMLElement($dom->saveXML());
//$formatxml->saveXML("testF.xml"); // save as file
return $formatxml->saveXML();
} // formatXML
Two different issues here:
Set the formatOutput and preserveWhiteSpace attributes to TRUE to generate formatted XML:
$doc->formatOutput = TRUE;
$doc->preserveWhiteSpace = TRUE;
Many web browsers (namely Internet Explorer and Firefox) format XML when they display it. Use either the View Source feature or a regular text editor to inspect the output.
See also xmlEncoding and encoding.
This is a slight variation of the above theme but I'm putting here in case others hit this and cannot make sense of it ...as I did.
When using saveXML(), preserveWhiteSpace in the target DOMdocument does not apply to imported nodes (as at PHP 5.6).
Consider the following code:
$dom = new DOMDocument(); //create a document
$dom->preserveWhiteSpace = false; //disable whitespace preservation
$dom->formatOutput = true; //pretty print output
$documentElement = $dom->createElement("Entry"); //create a node
$dom->appendChild ($documentElement); //append it
$message = new DOMDocument(); //create another document
$message->loadXML($messageXMLtext); //populate the new document from XML text
$node=$dom->importNode($message->documentElement,true); //import the new document content to a new node in the original document
$documentElement->appendChild($node); //append the new node to the document Element
$dom->saveXML($dom->documentElement); //print the original document
In this context, the $dom->saveXML(); statement will NOT pretty print the content imported from $message, but content originally in $dom will be pretty printed.
In order to achieve pretty printing for the entire $dom document, the line:
$message->preserveWhiteSpace = false;
must be included after the $message = new DOMDocument(); line - ie. the document/s from which the nodes are imported must also have preserveWhiteSpace = false.
based on the answer by #heavenevil
This function pretty prints using the browser
function prettyPrintXmlToBrowser(SimpleXMLElement $xml)
{
$domXml = new DOMDocument('1.0');
$domXml->preserveWhiteSpace = false;
$domXml->formatOutput = true;
$domXml->loadXML($xml->asXML());
$xmlString = $domXml->saveXML();
echo nl2br(str_replace(' ', ' ', htmlspecialchars($xmlString)));
}

Print XML node using XPath in PHP

I'm trying to print complex XML's node values using XPath, I have attached an image for helping to see the path which I need to reach (red underline).
Original XML file can be found here
I was trying something like that:
<?php
$xml = simplexml_load_file('document.xml');
echo "<strong>Using direct method...</strong><br />";
$names = $xml->xpath('/w:document/w:body/w:tbl[0]/w:tr[1]/w:tc[0]/w:p/w:r/w:t');
foreach($names as $name) {
echo "Found $name<br />";
}
?>
This method I am using to replace this node:
$file = "document.xml";
$fp = fopen($file, "rb") or die("error");
$str = fread($fp, filesize($file));
$xml = new DOMDocument();
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->loadXML($str) or die("Error");
$root = $xml->documentElement;
$fnode = $root->childNodes->item(0);
$ori = $fnode->childNodes->item(1);
$ori1 = $ori->childNodes->item(3);
$ori2 = $ori1->childNodes->item(1);
$ori3 = $ori2->childNodes->item(1);
$ori4 = $ori3->childNodes->item(1);
$ori5 = $ori4->childNodes->item(1);
$wt = $xml->createElement("w:t");
$wtText = $xml->createTextNode("".$name." ".$item."");
$wt->appendChild($wtText);
$ori4->replaceChild($wt,$ori5);
$xml->save("document.xml");
<?php
// Load XML
$doc = new DOMDocument();
$doc->load("document.xml");
// Use xpath to grab the node in question. I copied your xpath
// query as-is, assuming it was capable of targetting exactly
// the node you are trying to replace. If it returns more than
// one node, then only the first will be replaced.
// If this isn't what you want, I suggest modifying your xpath
// query to match exactly the single node you want to replace.
$xpath = new DOMXPath($doc);
$oldElement = $xpath->query("/w:document/w:body/w:tbl[0]/w:tr[1]/w:tc[0]/w:p/w:r/w:t")->item(0);
$newElement = $doc->createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t", $name . " " . $item);
// Replace old element with new element
$oldElement->parentNode->replaceChild($newElement, $oldElement);
?>

How to load a xml file in php so that i can use xpath on it?

I have a problem with php,
If I implement this code below then nothing will be happen.
$filename = "/opt/olat/olatdata/bcroot/course/85235053647606/runstructure.xml";
if (file_exists($filename)) {
$xml = simplexml_load_file($filename, 'SimpleXMLElement', LIBXML_NOCDATA);
// $xpath = new DOMXPath($filename);
}
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXpath($doc);
$res = $xpath->query('/org.olat.course.Structure/rootNode/children/org.olat.course.nodes.STCourseNode/shortTitle');
foreach ($res as $entry) {
echo "{$entry->nodeValue}<br/>";
}
If I change the contents of $xml in the content with the content of the $filename
$xml = '<org.olat.course.Structure><rootNode class="org.olat.course.nodes.STCourseNode"> ... ';
then it works, so i think that there is something wrong with loading methode of the xml file,
I've also tried to load the xml file as a Domdocument but it won't work neither.
And in both cases, it does work if I collect xml data via xml
for example this works
echo $Course_name = $xml->rootNode->longTitle;
loadXML takes a string as input, not the return value of simplexml_load_file. Just use file_get_contents to get the (full) contents of a file as string

Updating existing element in XML with PHP

Currently, im having this for appending data to my items file:
$xmldoc = new DOMDocument();
$xmldoc->load('ex.xml');
$item= $xmldoc->createElement('item');
$item->setAttribute('id', '100');
$item->setAttribute('category', 'Fitness');
$item->setAttribute('name', 'Basketball');
$item->setAttribute('url', 'http://google.com');
$item->setAttribute('description', 'This is a description');
$item->setAttribute('price', '899');
$xmldoc->getElementsByTagName('items')->item(0)->appendChild($item);
$xmldoc->save('ex.xml');
Now before appending this, I would like to check for an existing element "item" that has the same attribute id value.
And if it does it should update that element with these new data.
Currently it just appends and doesnt check anything.
$xmldoc = new DOMDocument();
$xmldoc->load('ex.xml');
$xpath = new DOMXPath($xmldoc);
$query = $xpath->query('/mainXML/items/item[#id = "100"]');
$create_new_node = false;
if($query->length == 0)
{
$item = $xmldoc->createElement('item');
$create_new_node = true;
}
else
{
$item = $query->item(0);
}
$item->setAttribute('id', '100');
$item->setAttribute('category', 'Fitness');
$item->setAttribute('name', 'Basketball');
$item->setAttribute('url', 'http://google.com');
$item->setAttribute('description', 'This is a description');
$item->setAttribute('price', '899');
if($create_new_node)
{
$xmldoc->getElementsByTagName('items')->item(0)->appendChild($item);
}
$xmldoc->save('ex.xml');
I haven't used this functionality but looks like a good match for DOMDocument: Get Element By ID
If you get a matching element, edit it, and if not, post away.
If you have a DTD for this xml file that specifies that the "id" attribute is an ID type (i.e. its value is unique in a document and uniquely identifies its element), then you can use DOMDocument::getElementById().
Most likely, however, you do not have a DTD. In this case, you should just use XPath:
$xmldoc = new DOMDocument();
$xmldoc->load('ex.xml');
$xpath = new DOMXPath($xmldoc);
$results = $xpath->query('//items/item[#id=100][0]');
if (!$results->length) {
$item= $xmldoc->createElement('item');
$item->setAttribute('id', '100');
$item->setAttribute('category', 'Fitness');
$item->setAttribute('name', 'Basketball');
$item->setAttribute('url', 'http://google.com');
$item->setAttribute('description', 'This is a description');
$item->setAttribute('price', '899');
$xmldoc->getElementsByTagName('items')->item(0)->appendChild($item);
$xmldoc->save('ex.xml');
}
You should also consider using SimpleXML for this task. The way this xml is structured and manipulated would probably be better-suited to SimpleXML.

Categories