PHP unable to access Xpath node of a SimpleXMLElement Object - php

$x = new DomDocument();
$x->loadXML($responseXml);
$xml = simplexml_import_dom($x);
Outputting the array using print_r($xml) gives the following:
SimpleXMLElement Object
(
[Timestamp] => 2014-11-09T18:28:47.843Z
[Ack] => Success
[Version] => 897
[Build] => E897_UNI_API5_17253832_R1
[Store] => SimpleXMLElement Object
(
[Name] => test
[SubscriptionLevel] => Basic
[Description] => Welcome Message.
)
)
Using $xml->Store->Description outputs "Welcome Message."
When I use xpath to return the Description node using the following code, I get an empty array:
$xpath = new DOMXPath($x);
$result = $xpath->query("/Store/Description");
Why does this fail?

Its easier with simple xml.
$xml = new SimpleXMLElement($string);
$result = $xml->xpath('/Store/Description');
http://php.net/manual/en/simplexmlelement.xpath.php

Simple really, I just needed to register the namespace:
$xml->registerXPathNamespace('urn', 'ebay:apis:eBLBaseComponents');

Related

Pull values from SimpleXMLElement Object

I'm using the namecheap API to do some stuff, it's the first time I've used a API and I'm running into a bit of a problem.
This is what I have so far:
$ApiKey = "**********************";
$ApiUser = "*****";
$UserName = "*********";
$ClientIP = "********";
$NamecheapURI = "https://api.namecheap.com/xml.response";
$executionURL = $NamecheapURI."?ApiUser=".$ApiUser."&ApiKey=".$ApiKey."&UserName=".$UserName."&Command=namecheap.domains.check&ClientIp=".$ClientIP."&DomainList=".$domain;
$xml = simplexml_load_file($executionURL);
print_r($xml);
When print $xml I am returned simple XML objects:
SimpleXMLElement Object
(
[#attributes] => Array
(
[Status] => OK
)
[Errors] => SimpleXMLElement Object
(
)
[Warnings] => SimpleXMLElement Object
(
)
[RequestedCommand] => namecheap.domains.check
[CommandResponse] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Type] => namecheap.domains.check
)
[DomainCheckResult] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Domain] => facebook.com
[Available] => false
[ErrorNo] => 0
[Description] =>
[IsPremiumName] => false
[PremiumRegistrationPrice] => 0
[PremiumRenewalPrice] => 0
[PremiumRestorePrice] => 0
[PremiumTransferPrice] => 0
[IcannFee] => 0
[EapFee] => 0
)
)
)
[Server] => PHX01APIEXT03
[GMTTimeDifference] => --5:00
[ExecutionTime] => 0.008
)
My question is beyond this, how do I move forward and pull data from this?
I've tried treating this as an array but I am getting nowhere, when using is_array() to test if it was an array it says it's not which I don't understand...
I apologise if this is a noob question, I am a bit new to this. In short, what do I need to do to pull data from this?
Thanks in advance!
Learning to use SimpleXML is much better than trying to convert it to arrays/json/anything else and simple (hence the name). A quick example...
$response = '<?xml version="1.0" encoding="UTF-8"?>
<CommandResponse Type="namecheap.domains.check">
<DomainCheckResult Domain="facebook.com">
<Element>1234</Element>
<Element>12345</Element>
</DomainCheckResult>
</CommandResponse>';
$xml = simplexml_load_string($response);
echo "DOmain=".$xml->DomainCheckResult['Domain'].PHP_EOL;
foreach ( $xml->DomainCheckResult->Element as $value) {
echo "Value=".(string)$value.PHP_EOL;
}
outputs...
DOmain=facebook.com
Value=1234
Value=12345
You have to adapt this to your own XML, but the idea is that if you want to access an element of an item you use object notation -> and if you need to get an attribute, use array notation [].
So in the above code, the first echo ($xml->DomainCheckResult['Domain']) gets the <DomainCheckResult> element and outputs the Domain attribute.
Then the foreach loop says fetch each <Element> within <DomainCheckResult> and output the value.

How to get an element from simplexml [duplicate]

This question already has answers here:
SimpleXML get node value
(3 answers)
Closed 8 years ago.
SimpleXMLElement Object
(
[SMSMessage] => SimpleXMLElement Object
(
[Sid] => xyz
[DateUpdated] => 2013-05-02 18:43:19
[DateCreated] => 2013-05-02 18:43:19
[DateSent] => 1970-01-01 05:30:00
[AccountSid] => xx
[To] => 09011148771
[From] => xx
[Body] => Hello
[BodyIndex] => SimpleXMLElement Object
(
)
[Status] => sending
[Direction] => outbound-api
[Price] => SimpleXMLElement Object
(
)
[ApiVersion] => SimpleXMLElement Object
(
)
[Uri] => /v1/Accounts/xx/Sms/Messages/xyz
)
)
I tried:
$xml->Sid;
But it returns SimpleXMLElement Object ( )
I also tried $xml->title, which also returned the same SimpleXMLElement Object ( )
How to get the Sid from the above XMl
I've been Fiddling a bit and recreated a structure similar to yours:
$string='<?xml version="1.0"?>
<xml>
<SMSMessage>
<Sid>xyz</Sid>
<DateUpdated>2013-05-02 18:43:19</DateUpdated>
</SMSMessage>
</xml>';
$xml = new SimpleXMLElement($string);
print_r($xml);
this outputs:
SimpleXMLElement Object
(
[SMSMessage] => SimpleXMLElement Object
(
[Sid] => xyz
[DateUpdated] => 2013-05-02 18:43:19
)
)
Which is equal to yours. And I could print xyz doing:
echo $xml->SMSMessage->Sid;
Try it out, you might be missing some parent node or something.
Your Method: To do it the way you want, I think you'd have to go one step further with your call since the SimpleXMLObject is parent to another SimpleXMLObject. E.g. $xml->SMSMessage->Sid;. I generally recommend using xpath with XML because in most cases, you want to jump directly to a specific node and not traverse the whole XML tree. For example, this: $xml->xpath('//[node]') is quicker than $xml->tier1->tier2->tier3->etc.
Preferred Method: Assuming $xml represents the SimpleXMLObject you've posted, you can access Sid like this: $xml->xpath('//Sid');. This should skip directly to the "Sid" node in the tree.
In simplexml you always have to cast your values - so you have to do this:
echo $xml->Sid;
(echo automatically casts). Or explicitly:
$string = (string) $xml->id;
if you have:
$string='<?xml version="1.0"?>
<xml>
<SMSMessage>
<Sid>xyz</Sid>
<DateUpdated>2013-05-02 18:43:19</DateUpdated>
</SMSMessage>
</xml>';
$xml = new SimpleXMLElement($string);
print_r('<pre>');
$Sid = (array)($xml->SMSMessage->Sid);
print_r($Sid[0]);
print_r($xml);
You can access Sid like so $Sid = (array)(xml->SMSMessage->Sid); echo $Sid[0];
But if you rather use array you can do this:
$string='<?xml version="1.0"?>
<xml>
<SMSMessage>
<Sid>xyz</Sid>
<DateUpdated>2013-05-02 18:43:19</DateUpdated>
</SMSMessage>
</xml>';
$array= json_decode(json_encode(new SimpleXMLElement($string)), true);
print_r('<pre>');
print_r($array['SMSMessage']['Sid']);
print_r($array);

How can i access simplexml elements from this list?

So i have these simplexmlelement objects. And i cant get it to work how to parse a specific element.
SimpleXMLElement Object
(
[Generation] => SimpleXMLElement Object
(
[#attributes] => Array
(
[version] => 3.1.0-alpha3
[timestamp] => 1355434832
)
)
[Options] => SimpleXMLElement Object
(
[#attributes] => Array
(
[tempFormat] => c
[byteFormat] => auto_binary
[refresh] => 60000
[showPickListTemplate] => true
[showPickListLang] => true
)
)
[UsedPlugins] => SimpleXMLElement Object
(
)
[Vitals] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Hostname] => domain.tld
[IPAddr] => 127.0.0.1
[Kernel] => 2.6.32-11-pve (SMP) x86_64
[Distro] => Ubuntu 12.04.1 LTS
[Distroicon] => Ubuntu.png
[Uptime] => 1993669.51
[Users] => 1
[LoadAvg] => 0.08 0.02 0.01
[CPULoad] => 0
)
)
....etc...
)
I have made something like this to access the Hostname for example:
echo $xml->Generation->Vitals[0]->Hostname;
But i think i am doing something wrong. Could someone point me in the right direction?
It would just be:
$xml->Vitals[0]->attributes()->Hostname
I am not entirely sure if you have to stick with a SimpleXml object, but if you don't, have a look at the DOMDocument and DOMXPath combination. I really like it. Just to be clear, it's hard to get into, but as soon as you get the hang of it, you are going to like it.
You can just do something like this:
$doc = new DOMDocument();
$doc->load('http://www.example.com/file.xml');
$xpath = new DOMXPath($doc);
$result = $xpath->query('//Vitals');
// print them all
foreach ($result as $r){
echo $r->getAttribute('Hostname'); //your desired value
}
// or just the first one
echo $result->item(0)->getAttribute('Hostname');
You could also make the query slightly bigger to get the attribute right away like so: //Vitals#Hostname which should work as well.
And with this start you will possibly get the hang of it.
Further reading:
http://php.net/manual/en/class.domdocument.php
http://php.net/manual/en/class.domxpath.php
http://php.net/manual/en/domxpath.query.php
http://www.tizag.com/xmlTutorial/xpathattribute.php
http://www.w3schools.com/xpath/xpath_syntax.asp

How to diff SimpleXML multidimensional array?

I have a SimpleXML Object made from merging multiple XMLs from PubMed (snippet below) but there is repetition from the merge. How can I compare all first child arrays - array[][0], array[][1] etc - and discard any duplicates?
I though perhaps serialising was the answer but you can't serialise a SimpleXML Object afaik..
I'm not sure where to start?
Array
(
[0] => Array
(
[title] => SimpleXMLElement Object
(
[0] => Superstructure of the centromeric complex of TubZRC plasmid partitioning systems.
)
[link] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Version] => 1
)
[0] => 23010931
)
[author] => Aylett, CH., Löwe, J.
[journal] => SimpleXMLElement Object
(
[0] => Proc. Natl. Acad. Sci. U.S.A.
)
[pubdate] => 2012-9-27
[day] => SimpleXMLElement Object
(
[0] => 25
)
[month] => SimpleXMLElement Object
(
[0] => Sep
)
[year] => SimpleXMLElement Object
(
[0] => 2012
)
)
[1] => Array
(
[title] => SimpleXMLElement Object
(
[0] => Superstructure of the centromeric complex of TubZRC plasmid partitioning systems.
)
[link] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Version] => 1
)
[0] => 23010931
)
[author] => Aylett, CH., Löwe, J.
[journal] => SimpleXMLElement Object
(
[0] => Proc. Natl. Acad. Sci. U.S.A.
)
[pubdate] => 2012-9-27
[day] => SimpleXMLElement Object
(
[0] => 25
)
[month] => SimpleXMLElement Object
(
[0] => Sep
)
[year] => SimpleXMLElement Object
(
[0] => 2012
)
)
Alternatively it could be done at the initial XML merge stage - I use the code below at the moment if anyone can suggest how to modify it to remove duplicates?
function simplexml_merge (SimpleXMLElement &$xml1, SimpleXMLElement $xml2) {
$dom1 = new DomDocument();
$dom2 = new DomDocument();
$dom1->loadXML($xml1->asXML());
$dom2->loadXML($xml2->asXML());
$xpath = new domXPath($dom2);
$xpathQuery = $xpath->query('/*/*');
for ($i = 0; $i < $xpathQuery->length; $i++) {
$dom1->documentElement->appendChild(
$dom1->importNode($xpathQuery->item($i), true));
}
$xml1 = simplexml_import_dom($dom1);
}
$xml1 = new SimpleXMLElement($search1);
$xml2 = new SimpleXMLElement($search2);
simplexml_merge($xml1, $xml2);
Thanks.
...
...
For clarity - here's the XML source layout that I am importing into SimpleXML - each PubmedArticle is one "element" I am interested in comparing and ensuring there are no duplicates -
<xml...>
<Document>
<PubmedArticle>
<MedlineCitation>
<PMID version="1">xxx</PMID>
...
</MedlineCitation>
...
</PubmedArticle>
<PubmedArticle>
<MedlineCitation>
<PMID version="1">xxx</PMID>
...
</MedlineCitation>
...
</PubmedArticle>
etc
</Document>
</xml>
The PMID node is unique so can be used to check for duplicates.
...
...
Using the link from #Gordon - I know use:
//Get my source XML
$xml1 = new SimpleXMLElement($search1);
$xml2 = new SimpleXMLElement($search2);
//Run through $xml1 and build a query based on it's PMIDs
$query = array();
foreach ($xml1->PubmedArticle as $paper) {
$query[] = sprintf('(PMID != %s)',$paper->MedlineCitation->PMID);
}
$query = implode('and', $query);
//Run through $xml2 and get node which don't have PMID matching $xml1
foreach ($xml2->xpath(sprintf('PubmedArticle/MedlineCitation[%s]', $query)) as $paper) {
echo $paper->asXml();
}
However I still have one problem - getting the output merged.
The output of $xml2 is missing the <PubmedArticle> node around each 'match' for a start. Then I presume I can use the same merge code (above) to do the merge.
Can you point me in the right direction?
Convert it to an array (which I'm not going to write for you, just iterate and add.), then array_diff().
Decided to follow #Gordon's line as it kept it XML. Eventually got it all working:
//function to check 2 xml inputs for duplicate nodes
function dedupeXML($xml1, $xml2) {
$query = array();
foreach ($xml1->PubmedArticle as $paper) {
$query[] = sprintf('(MedlineCitation/PMID != %s)',$paper->MedlineCitation->PMID);
}
$query = implode('and', $query);
$xmlClean = '<Document>';
foreach ($xml2->xpath(sprintf('PubmedArticle[%s]', $query)) as $paper) {
$xmlClean .= $paper->asXML();
}
$xmlClean .= '</Document>';
$xmlClean = new SimpleXMLElement($xmlClean);
return $xmlClean;
}
//function to merge 2 xml inputs
function mergeXML (SimpleXMLElement &$xml1, SimpleXMLElement $xml2) {
// convert SimpleXML objects into DOM ones
$dom1 = new DomDocument();
$dom2 = new DomDocument();
$dom1->loadXML($xml1->asXML());
$dom2->loadXML($xml2->asXML());
// pull all child elements of second XML
$xpath = new domXPath($dom2);
$xpathQuery = $xpath->query('/*/*');
for ($i = 0; $i < $xpathQuery->length; $i++) {
// and pump them into first one
$dom1->documentElement->appendChild(
$dom1->importNode($xpathQuery->item($i), true));
}
$xml = simplexml_import_dom($dom1);
return $xml;
}
$xml1 = new SimpleXMLElement($search1);
$xml2 = new SimpleXMLElement($search2);
$xml3 = new SimpleXMLElement($search3);
//dedupe and merge inputs
//input 1 & 2
$xml2Clean = dedupeXML($xml1, $xml2);
$xml12 = mergeXML($xml1, $xml2Clean);
//input 1+2 & 3
$xml3Clean = dedupeXML($xml12, $xml3);
$xml123 = mergeXML($xml12, $xml3Clean);
This would be easy to adapt to other data sources - just modify the dedupeXML function to match the data structure of your XML.

How to break down and parse specific Wikipedia text

I'm have the following working example to retrieve a specific Wikipedia page that returns a SimpleXMLElement Object:
ini_set('user_agent', 'michael#example.com');
$doc = New DOMDocument();
$doc->load('http://en.wikipedia.org/w/api.php?action=parse&page=Main%20Page&format=xml');
$xml = simplexml_import_dom($doc);
print '<pre>';
print_r($xml);
print '</pre>';
Which returns:
SimpleXMLElement Object
(
[parse] => SimpleXMLElement Object
(
[#attributes] => Array
(
[title] => Main Page
[revid] => 472210092
[displaytitle] => Main Page
)
[text] => <body><table id="mp-topbanner" style="width: 100%;"...
Silly question/mind blank. What I am trying to do is capture the $xml->parse->text element and in-turn parse that. So ultimately what I want returned is the following object; how do I achieve this?
SimpleXMLElement Object
(
[body] => SimpleXMLElement Object
(
[table] => SimpleXMLElement Object
(
[#attributes] => Array
(
[id] => mp-topbanner
[style] => width:100% ...
After grabbing a fresh tea and eating a banana, here's the solution I've come up with:
ini_set('user_agent','michael#example.com');
$doc = new DOMDocument();
$doc->load('http://en.wikipedia.org/w/api.php?action=parse&page=Main%20Page&format=xml');
$nodes = $doc->getElementsByTagName('text');
$str = $nodes->item(0)->nodeValue;
$html = new DOMDocument();
$html->loadHTML($str);
This then allows me to get an elements value, which is what I was after. For example:
echo "Some value: ";
echo $html->getElementById('someid')->nodeValue;

Categories