Accessing XML data within namespaces - php

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');

Related

Modify XML in PHP

I have the xml below
<?xml version="1.0" encoding="UTF-8"?>
<!--Sample XML file generated by XMLSpy v2013 (http://www.altova.com)-->
<ftc:FATCA_OECD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ftc="urn:oecd:ties:fatca:v2" xmlns:sfa="urn:oecd:ties:stffatcatypes:v2" version="2.0" xsi:schemaLocation="urn:oecd:ties:fatca:v2 FatcaXML_v2.0.xsd">
<ftc:MessageSpec>
<sfa:SendingCompanyIN>S519K4.99999.SL.392</sfa:SendingCompanyIN>
<sfa:TransmittingCountry>JP</sfa:TransmittingCountry>
<sfa:ReceivingCountry>US</sfa:ReceivingCountry>
<sfa:MessageType>FATCA</sfa:MessageType>
<sfa:MessageRefId>DBA6455E-8454-47D9-914B-FEE48E4EF3AA</sfa:MessageRefId>
<sfa:ReportingPeriod>2016-12-31</sfa:ReportingPeriod>
<sfa:Timestamp>2017-01-17T09:30:47Z</sfa:Timestamp>
<ftc:SendingCompanyIN>testing</ftc:SendingCompanyIN></ftc:MessageSpec>
<ftc:FATCA>
<ftc:ReportingFI>
<sfa:ResCountryCode>JP</sfa:ResCountryCode>
<sfa:TIN>S519K4.99999.SL.392</sfa:TIN>
<sfa:Name>Bank of NN</sfa:Name>
<sfa:Address>
<sfa:CountryCode>JP</sfa:CountryCode>
<sfa:AddressFree>123 Main Street</sfa:AddressFree>
</sfa:Address>
<ftc:DocSpec>
<ftc:DocTypeIndic>FATCA1</ftc:DocTypeIndic>
<ftc:DocRefId>S519K4.99999.SL.392.50B80D2D-79DA-4AFD-8148-F06480FFDEB5</ftc:DocRefId>
</ftc:DocSpec>
</ftc:ReportingFI>
<ftc:ReportingGroup>
<ftc:NilReport>
<ftc:DocSpec>
<ftc:DocTypeIndic>FATCA1</ftc:DocTypeIndic>
<ftc:DocRefId>S519K4.99999.SL.392.CE54CA78-7C31-4EC2-B73C-E387C314F426</ftc:DocRefId>
</ftc:DocSpec>
<ftc:NoAccountToReport>yes</ftc:NoAccountToReport>
</ftc:NilReport>
</ftc:ReportingGroup>
</ftc:FATCA>
</ftc:FATCA_OECD>
I want to change node value, sfa:TIN and save the xml in a new file. How can this be accomplished in PHP? I got examples but none used namespaces.
One way you could do this is using DOMDocument and DOMXPath and find your elements using for example an xpath expression which will find the 'TIN' elements in the sfa namespace.
/ftc:FATCA_OECD/ftc:FATCA/ftc:ReportingFI/sfa:TIN
To update the value of the first found elemement you could take the first item from the DOMNodeList which is returned by query.
$doc = new DOMDocument();
$doc->loadXML($data);
$xpath = new DOMXPath($doc);
$res = $xpath->query("/ftc:FATCA_OECD/ftc:FATCA/ftc:ReportingFI/sfa:TIN");
if ($res->length > 0) {
$res[0]->nodeValue = "test";
}
$doc->save("yourfilename.xml");
Demo
You can use the following solution, using DOMDocument::getElementsByTagNameNS:
<?php
$dom = new DOMDocument();
$dom->load('old-file.xml');
//get all TIN nodes.
$nodesTIN = $dom->getElementsByTagNameNS('urn:oecd:ties:stffatcatypes:v2', 'TIN');
//check for existing TIN node.
if (count($nodesTIN) === 1) {
//update the first TIN node.
$nodesTIN->item(0)->nodeValue = 'NEWVALUE_OF_TIN';
}
//save the file to a new one.
$dom->save('new-file.xml');

Read XML File with DOMDocument in 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.

Getting specific xml data on php with xpath

I have a xml response like that;
<n:Crev xmlns:soap="http://a.com"
xmlns:obj="http://b.com"
xmlns:n="http://c.com"
xmlns:msg="http://d.com"
xmlns="http://e.com"
xmlns:xsi="http://f.com"
xsi:schemaLocation="http://g.com">
<n:Header>
<msg:mydata>123123</msg:mydata>
</n:Header>
</n:Crev>
now I want to get 'msg:mydata' value..
I tried some xpaths but they didn't work and tried online xpath creator it gives something like;
'/n:Crev[#xmlns:soap="http://a.com"]/n:Header/msg:mydata/text()'
but it didn't work also.. So how can I write xpath for that?
Thanks
I've succeeded with following code:
<?php
$xmlStr = '<n:Crev xmlns:soap="http://a.com"
xmlns:obj="http://b.com"
xmlns:n="http://c.com"
xmlns:msg="http://d.com"
xmlns="http://e.com"
xmlns:xsi="http://f.com"
xsi:schemaLocation="http://g.com">
<n:Header>
<msg:mydata>123123</msg:mydata>
</n:Header>
</n:Crev>';
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xmlStr);
$xmlPath = new DOMXPath($xmlDoc);
var_dump($xmlPath->query('//n:Crev/n:Header/msg:mydata')->item(0)->textContent);
result:
string '123123' (length=6)
n or msg are namespace prefixes. The actual namespaces are the values of the xmlns attributes. The XML parser will resolve the namespaces.
Here is a small example:
$document = new DOMDocument();
$document->loadXml('<n:Crev xmlns:n="http://c.com"/>');
var_dump(
$document->documentElement->namespaceURI,
$document->documentElement->localName
);
Output:
string(12) "http://c.com"
string(4) "Crev"
The following XMLs all would have the same output:
<n:Crev xmlns:n="http://c.com"/>
<Crev xmlns="http://c.com"/>
<c:Crev xmlns:c="http://c.com"/>
You can read the node as {http://c.com}Crev.
To fetch nodes or scalar values from the DOM you can use Xpath::evaluate(). But to match namespaces you will have to register prefixes for the Xpath expressions. This allows the Xpath engine to resolve the namespaces and match them against the node properties. The prefixes do not have to be the same as in the document.
$xml = <<<'XML'
<n:Crev xmlns:n="http://c.com" xmlns:msg="http://d.com">
<n:Header>
<msg:mydata>123123</msg:mydata>
</n:Header>
</n:Crev>
XML;
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('c', 'http://c.com');
$xpath->registerNamespace('msg', 'http://d.com');
var_dump(
$xpath->evaluate('string(/c:Crev/c:Header/msg:mydata)')
);
Output:
string(6) "123123"
If the expression is an location path like /c:Crev/c:Header/msg:mydata the result with be an DOMNodeList, but Xpath functions or operators can return scalar values.

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

php parsing xml formatted data

I'm trying to parse an xml data that I'm getting via an api call. I can use file_get_contents to read into a string but simpleXML_load_string seems to fail to read it. I can save it to a file and then simpleXML_load_file works. But I would rather not write the contents to a file. I can't seem to understand how to use DOM or XMLParse with this either. I'm new to PHP and parsing XML. The output data from the api call is below.
<Search>
<DS_Rating>DS3</DS_Rating>
<Overall>17.5</Overall>
<LargestGiftLow>0</LargestGiftLow>
<LargestGiftHigh>0</LargestGiftHigh>
<EstimatedCapacityRange>I - $15,000 - $24,999</EstimatedCapacityRange>
<EstimatedCapacity>20452</EstimatedCapacity>
<RealEstateEst>270073</RealEstateEst>
<RealEstateCount>1</RealEstateCount>
<LikelyMatchesCount>0</LikelyMatchesCount>
<LikelyMatchesTotal>0</LikelyMatchesTotal>
<FndBoard></FndBoard>
<GSBoard></GSBoard>
<PoliticalLikelyCount>0</PoliticalLikelyCount>
<PoliticalLikelyTotal>0</PoliticalLikelyTotal>
<BusinessRevenues>0</BusinessRevenues>
<SECStockValue>0</SECStockValue>
<SECInsider></SECInsider>
<MarketGuide></MarketGuide>
<IRS990PF></IRS990PF>
<RealEstateTrust></RealEstateTrust>
<MarketGuideComp>0</MarketGuideComp>
<MarketGuideOptions>0</MarketGuideOptions>
<BusinessAffiliation></BusinessAffiliation>
<Pension></Pension>
<PensionAssets>0</PensionAssets>
<CorpTech></CorpTech>
<Pilot></Pilot>
<AirplaneOwner></AirplaneOwner>
<Boat></Boat>
<submit_time>2014-03-11 15:48:45</submit_time>
</Search>
Figured out that the issue was that what I was seeing in the browser was actually a php output with html_entiity encoded. I was able to process it with the code below which let me load it with simplexml.
$rawxml = html_entity_decode($rawxml);
$rawxml = str_replace(array(' ', "<pre>"), '', $rawxml);
$rawxml = utf8_encode($rawxml);
$xml = simplexml_load_string($rawxml);
If you XML is in a file use
simplexml_load_file
if you have it in a string use
simplexml_load_string
Then you can use the following code to access it.
<?php
$yourxml = simplexml_load_file('your.xml');
echo $yourxml->search[0]->DS_Rating;
?>
This would then output
DS3
to the browser via the 'echo' in your code. I hope this points you in the correct direction.
Try to use this:
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?>'.$yourXMLString);
In DOM you load the XML into a DOMDocument and create a DOMXpath instance for it.
$dom = new DOMDocument();
$dom->loadXml($xmlString);
//$dom->load($xmlFile);
$xpath = new DOMXpath($dom);
DOMXpath::evaluate() is used to fetch data from the DOM.
$rating = $dom->evaluate('string(/Search/DS_Rating)');
An Xpath expression like /Search/DS_rating always returns a node list. You can use foreach() to iterate it. The string() function in Xpath takes the first node from the list and casts it into a string. If here is not node in the list the result is an empty string.
$xmlString = <<<'XML'
<Search>
<DS_Rating>DS3</DS_Rating>
<Overall>17.5</Overall>
</Search>
XML;
$dom = new DOMDocument();
$dom->loadXml($xmlString);
$xpath = new DOMXpath($dom);
var_dump(
$xpath ->evaluate('string(/Search/DS_Rating)')
);
Output: https://eval.in/118921
string(3) "DS3"

Categories