Read XML File with DOMDocument in php - php

I want to read this xml document:
<?xml version="1.0" encoding="UTF-8"?>
<tns:getPDMNumber xmlns:tns="http://www.testgroup.com/TestPDM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.testgroup.com/TestPDM getPDMNumber.xsd ">
<tns:getPDMNumberResponse>
<tns:requestID>22222</tns:requestID>
<tns:pdmNumber>654321</tns:pdmNumber>
<tns:responseCode>0</tns:responseCode>
</tns:getPDMNumberResponse>
</tns:getPDMNumber>
I tried it this way:
$dom->load('response/17_getPDMNumberResponse.xml');
$nodes = $dom->getElementsByTagName("tns:requestID");
//$nodes = $dom->getElementsByTagName("tns:getPDMNumber");
//$nodes = $dom->getElementsByTagName("tns:getPDMNumberResponse");
foreach($nodes as $node)
{
$response=$node->getElementsByTagName("tns:getPDMNumber");
foreach($response as $info)
{
$test = $info->getElementsByTagName("tns:pdmNumber");
$pdm = $test->nodeValue;
}
}
the code never runs into the foreach loop.
Only for clarification my goal is to read the "tns:pdmNumber" node.
Have anybody a idea?
EDIT: I have also tried the commited lines.

The XML uses a namespace, so you should use the namespace aware methods. They have the suffix _NS.
$tns = 'http://www.testgroup.com/TestPDM';
$document = new DOMDocument();
$document->loadXml($xml);
foreach ($document->getElementsByTagNameNS($tns, "pdmNumber") as $node) {
var_dump($node->textContent);
}
Output:
string(6) "654321"
A better option is to use Xpath expression. They allow a more comfortable access to DOM nodes. In this case you have to register a prefix for the namespace that you can use in the Xpath expression:
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('t', 'http://www.testgroup.com/TestPDM');
var_dump(
$xpath->evaluate('string(/t:getPDMNumber/t:getPDMNumberResponse/t:pdmNumber)')
);

This:
$nodes = $dom->getElementsByTagName("tns:requestID");
you find all the requestID nodes, and try to loop on them. That's fine, but then you use that node as a basis to find any getPDMNumber nodes UNDER the requestID - but there's nothing - requestID is a terminal node. So
$response=$node->getElementsByTagName("tns:getPDMNumber");
finds nothing, and the inner loop has nothing to do.
It's like saying "Start digging a hole until you reach china. Once you reach China, keep digging until you reach Australia". But you can't keep digging - you've reached the "bottom", and the only thing deeper than China would be going into orbit.

Related

Accessing XML data within namespaces

So my XML Looks like this :-
<ns0:ASN xmlns:ns0="http://schemas.microsoft.com/dynamics/2008/01/documents/ASN" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:CustPackingSlipJour class="entity">
<ns0:BON_FileNameSeqNum>40</ns0:BON_FileNameSeqNum>
<ns0:BON_TotalNetAmount>10.00</ns0:BON_TotalNetAmount>
<ns0:BON_TotalTaxAmount>.00</ns0:BON_TotalTaxAmount>
<ns0:InvoiceAccount>Acc</ns0:InvoiceAccount>
<ns0:LanguageId>EN</ns0:LanguageId>
<ns0:OrderAccount>I</ns0:OrderAccount>
<ns0:PurchaseOrder>74</ns0:PurchaseOrder>
<ns0:Qty>13.00</ns0:Qty>
<ns0:SalesId>00025873_054</ns0:SalesId>
<ns0:CustPackingSlipTrans class="entity">
<ns0:BON_LineNetAmount>19.00</ns0:BON_LineNetAmount>
<ns0:BON_SalesPrice>0.00</ns0:BON_SalesPrice>
<ns0:DeliveryDate>2016-11-30</ns0:DeliveryDate>
<ns0:ItemId>25712</ns0:ItemId>
<ns0:Ordered>1.00</ns0:Ordered>
<ns0:PackingSlipId>00339_061</ns0:PackingSlipId>
<ns0:Qty>1.00</ns0:Qty>
</ns0:CustPackingSlipTrans>
<ns0:CustPackingSlipTrans class="entity">
<ns0:BON_LineNetAmount>19.00</ns0:BON_LineNetAmount>
<ns0:BON_SalesPrice>0.00</ns0:BON_SalesPrice>
<ns0:DeliveryDate>2-11-30</ns0:DeliveryDate>
<ns0:ItemId>25823-35714</ns0:ItemId>
<ns0:Ordered>1.00</ns0:Ordered>
<ns0:PackingSlipId>00_061</ns0:PackingSlipId>
<ns0:Qty>1.00</ns0:Qty>
</ns0:CustPackingSlipTrans>
</ns0:CustPackingSlipJour>
</ns0:ASN>
How can I access the value of ItemId for all CustPackingSlipTrans ?
I have tried various ways of getting it, for instance registering xpath and then trying to access. However, it ins't working for me. Whats the best way to get it's value?
The solution using DOMXPath::query method:
// $xml contains your xml contents
$doc = new \DOMDocument();
$doc->loadXML($xml);
$xpath = new \DOMXPath($doc);
foreach ($xpath->query("ns0:CustPackingSlipJour/ns0:CustPackingSlipTrans/ns0:ItemId") as $node) {
var_dump($node->nodeValue);
}
The output:
string(5) "25712"
string(11) "25823-35714"
DEMO
You need to register the namespace with the DomXPath:
$xp = new DomXPath ($doc);
$xp->registerNamespace ('pfx', 'http://pfxuri');

getting xml node with php

Im attempting to echo/assign a variable to the contents of the node "code" which is inside status;
I can get request-id just fine...
Any ideas people?
<?
$responseXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<payment xmlns="http://www.example.com" self="http://www.example.com">
<merchant-account-id ref="http://www.example.com">0000</merchant-account-id>
<transaction-id>0000</transaction-id>
<request-id>0000</request-id>
<transaction-type>auth</transaction-type>
<transaction-state>success</transaction-state>
<completion-time-stamp>2015-12-28T17:39:25.000Z</completion-time-stamp>
<statuses>
<status code="201.0000" description="3d-acquirer:The resource was successfully created." severity="information"/>
</statuses>
<avs-code>P</avs-code>
<requested-amount currency="GBP">0.01</requested-amount>
<account-holder>
<first-name>test</first-name>
<last-name>test</last-name>
<email>test.test#hotmail.co.uk</email>
<phone>00000000000</phone>
<address>
<street1>test</street1>
<city>test test</city>
<state>test</state>
<country>GB</country>
</address>
</account-holder>
<card-token>
<token-id>000</token-id>
<masked-account-number>000000******0000</masked-account-number>
</card-token>
<ip-address>192.168.0.1</ip-address>
<descriptor></descriptor>
<authorization-code>000000</authorization-code>
<api-id>000-000</api-id>
</payment>';
$doc = new DOMDocument;
$doc->loadXML($responseXML);
echo $doc->getElementsByTagName('request-id')->item(0)->nodeValue;
echo $doc->getElementsByTagName('status code')->item(0)->nodeValue;
?>
I've tried simplexml looad string, but pulling hair out with this one, can anybody shed some light, speed of getting this info out in one process is quite important so not to stress the webserver out!
Many thanks.
Using DOM is a good idea, but the API methods are a little cumbersome. Using Xpath makes it a lot easier.
Xpath allows you to use expressions to fetch node lists or scalar values from a DOM:
$document = new DOMDocument;
$document->loadXML($responseXML);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('example', 'http://www.example.com');
echo $xpath->evaluate('string(//example:request-id)'), "\n";
echo $xpath->evaluate('string(//example:status/#code)');
Output:
0000
201.0000
Xpath does not have a default namespace so if you XML has a namespace (like your example) you need to register a prefix for it and use it.
As code is an attribute of xml tag status, doing
getElementsByTagName('status code')
is wrong.
There's a special method for getting attribute value getAttribute:
echo $doc->getElementsByTagName('status')->item(0)->getAttribute('code');
Using XPath allows to access the status node very precisely.
DOMDocument + XPath:
$responseXML = '...';
$doc = new DOMDocument();
$doc->loadXML($responseXML);
$xp = new DOMXpath($doc);
$xp->registerNamespace('example', 'http://www.example.com');
// Every status node.
$statusNodes = $xp->query('//example:status');
// or a very specific one.
$statusNodes = $xp->query('/example:payment/example:statuses/example:status');
$statusNode = $statusNodes[0];
$code = $statusNode->getAttribute('code');
// $code is '201.0000'.
// To change the 'code' value.
$statusNode->setAttribute('code', '302.0000');

XPath Substring-After Help / Query/Evaluate?

I'm building a php script to transfer selected contents of an xml file to an sql database..
One of the hardcoded XML contents is formatted like this:
<visualURL>
id=18144083|img=http://upload.wikimedia.org/wikipedia/en/8/86/Holyrollernovacaine.jpg
</visualURL>
And I'm looking for a way to just get the contents of the URL (all text after img=).
$Image = $xpath->query("substring-after(/Playlist/PlaylistEntry[1]/visualURL[1]/text(), 'img=')", $element)->item(0)->nodeValue;
Displays a property non-object error on my php output.
There must be another way to just extract the URL contents using XPath that I want, no?
Any help would be greatly appreciated!
EDIT:
Here is the minimum code
<?php
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML('<Playlist>
<PlaylistEntry>
<visualURL>
id=12582194|img=http://upload.wikimedia.org/wikipedia/en/9/96/Sometime_around_midnight.jpg
</visualURL>
</PlaylistEntry>
</Playlist>');
$xpath = new DOMXpath($xmlDoc);
$elements = $xpath->query("/Playlist/PlaylistEntry[1]");
if (!is_null($elements))
foreach ($elements as $element)
$Image = $xpath->query("substring-after(/Playlist/PlaylistEntry[1]/visualURL[1]/text(), 'img=')", $element)- >item(0)->nodeValue;
print "Finished Item: $Image";
?>
EDIT 2:
After some research I believe I must use
$xpath->evaluate
instead of my current use of
$xpath->query
see this link
Same XPath query is working with Google docs but not PHP
I'm not exactly sure how to do this yet.. but i will investigate more in the morning. Again, any help would be appreciated.
You're in right direction. Use DOMXPath::evaluate() for xpath expression that doesn't return node(s) like substring-after() (it returns string as documented in the linked page). The following codes prints expected output :
$xmlDoc = new DOMDocument();
$xml = <<<XML
<Playlist>
<PlaylistEntry>
<visualURL>
id=12582194|img=http://upload.wikimedia.org/wikipedia/en/9/96/Sometime_around_midnight.jpg
</visualURL>
</PlaylistEntry>
</Playlist>
XML;
$xmlDoc->loadXML($xml);
$xpath = new DOMXpath($xmlDoc);
$elements = $xpath->query("/Playlist/PlaylistEntry");
foreach ($elements as $element) {
$Image = $xpath->evaluate("substring-after(visualURL, 'img=')", $element);
print "Finished Item: $Image <br>";
}
output :
Finished Item: http://upload.wikimedia.org/wikipedia/en/9/96/Sometime_around_midnight.jpg
Demo

SimpleXML how to get line number of a node?

I'm using this in SimpleXML and PHP:
foreach ($xml->children() as $node) {
echo $node->attributes('namespace')->id;
}
That prints the id attribute of all nodes (using a namespace).
But now I want to know the line number that $node is located in the XML file.
I need the line number, because I'm analyzing the XML file, and returning to the user information of possible issues to resolve them. So I need to say something like: "Here you have an error at line X". I'm sure that the XML file would be in a standard format that will have enough line breaks for this to be useful.
It is possible with DOM. DOMNode provides the function getLineNo().
DOM
$xml = <<<'XML'
<foo>
<bar/>
</foo>
XML;
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
var_dump(
$xpath->evaluate('//bar[1]')->item(0)->getLineNo()
);
Output:
int(2)
SimpleXML
SimpleXML is based on DOM, so you can convert SimpleXMLElement objects to DOMElement objects.
$element = new SimpleXMLElement($xml);
$node = dom_import_simplexml($element->bar);
var_dump($node->getLineNo());
And yes, most of the time if you have a problem with SimpleXML, the answer is to use DOM.
XMLReader
XMLReader has the line numbers internally, but here is no direct method to access them. Again you will have to convert it into a DOMNode. It works because both use libxml2. This will read the node and all its descendants into memory, so be careful with it.
$reader = new XMLReader();
$reader->open('data://text/xml;base64,'.base64_encode($xml));
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name== 'bar') {
var_dump($reader->expand()->getLineNo());
}
}

Finding number of nodes in PHP, DOM, XPath

I am loading HTML into DOM and then querying it using XPath in PHP. My current problem is how do I find out how many matches have been made, and once that is ascertained, how do I access them?
I currently have this dirty solution:
$i = 0;
foreach($nodes as $node) {
echo $dom->savexml($nodes->item($i));
$i++;
}
Is there a cleaner solution to find the number of nodes, I have tried count(), but that does not work.
You haven't posted any code related to $nodes so I assume you are using DOMXPath and query(), or at the very least, you have a DOMNodeList.
DOMXPath::query() returns a DOMNodeList, which has a length member. You can access it via (given your code):
$nodes->length
If you just want to know the count, you can also use DOMXPath::evaluate.
Example from PHP Manual:
$doc = new DOMDocument;
$doc->load('book.xml');
$xpath = new DOMXPath($doc);
$tbody = $doc->getElementsByTagName('tbody')->item(0);
// our query is relative to the tbody node
$query = 'count(row/entry[. = "en"])';
$entries = $xpath->evaluate($query, $tbody);
echo "There are $entries english books\n";

Categories