Getting xpath child node value - php

I am having trouble getting the text "TestTwo" where the parent has an attribute with Name=SN from the XML below. I am able to get the SimpleXMLElement, but cannot figure out how to get the child.
What is the best way to return the text "TestTwo"?
Thank you!
$xml = simplexml_load_string($XMLBELOW);
$xml->registerXPathNamespace('s','urn:oasis:names:tc:SAML:2.0:assertion');
$result = $xml->xpath("//s:Attribute[#Name='sn']");
var_dump( $result);
$XMLBELOW = <<<XML
<?xml version="1.0" ?>
<saml2:Assertion ID="SAML-4324423" IssueInstant="2012-09-24T17:49:39Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Issuer>
Test
</saml2:Issuer>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="Test">
Tester
</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData NotBefore="2012-09-24T17:48:39Z" NotOnOrAfter="2012-09-24T17:51:39Z"/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2012-09-24T17:48:39Z" NotOnOrAfter="2012-09-24T17:51:39Z"/>
<saml2:AuthnStatement AuthnInstant="2012-09-24T17:49:39Z" SessionNotOnOrAfter="2012-09-24T17:51:39Z">
<saml2:SubjectLocality Address="105.57.487.48"/>
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified
</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute Name="sn" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue>
TestTwo
</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="departmentNumber" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue>
OPERS
</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
XML;

How about this?
$result = $xml->xpath("//s:Attribute[#Name='sn']/*");
var_dump( $result[0]->__toString() ); // or just `echo $result[0];`
The updated expression will get you all children of your target element (you can narrow the result set further, if you'd like).
As xpath() method returns an array, you need to take some of its elements, and just call 'toString' on these objects to get its text. Here I've done with the first one.

Related

How to get attributes of XML root element using SimpleXMLElement

I have an XML file with the root element name wwwjob. This root element contains attributes, I need to access the value of the 'method' attribute to be able to update various database entries.
This is a little bit of a learning curve at the moment.
<?xml version="1.0" encoding="iso-8859-1"?>
<wwwjob id="32cca11IACH" method="Delete">
some more xml stuff
</wwwjob>
Ive tried:
<?php $xml = $vacancyXML->wwwjob['method']; ?>
This just gave me 'NULL'.
Ive also tried:
<?php $xml = $vacancyXML->getName(); ?>
This just spits out the name 'wwwjob'.
I need to store the method (Delete/Update/Add) as a variable for use in later parts of the function.
Thanks
When loadinG to simplexml, attributes of the root element became attributes of the simpleXml object. So, you can get it just
$str = '<?xml version="1.0" encoding="iso-8859-1"?>
<wwwjob id="32cca11IACH" method="Delete">
</wwwjob>';
$vacancyXML = simplexml_load_string($str);
echo $vacancyXML['method']; // Delete
demo
You can get the attributes of the root element with SimpleXMLElement::attributes():
$www = new SimpleXMLElement($xml);
echo $www->attributes()->method; // Delete
Demo

Adding child to XML file using PHP

While adding child, this error is thrown :
Cannot add child. Parent is not a permanent member of the XML tree.
I cannot resolve this.
This is my code :
if($visited=='FIRST')
{
$xml=new SimpleXMLElement("<xml/>");
$topology=$xml->addChild("Topology_Configuration");
$flavor=$topology->addChild("Flavor");
$networks=$topology->addChild("Networks");
$vms=$topology->addChild("VMs");
$vnfs=$topology->addChild("VNFs");
$xml->asXML('saddening.xml');
}
else
{
$xml= simplexml_load_file('saddening.xml');
$Topology_Configuration = new SimpleXMLElement($xml->asXML());
$vmcount=$_POST['arguments']['vmcount'];
$flavor=$Topology_Configuration->Flavor;
$flavor_name=$flavor->addChild($_POST['arguments']['flavorName']);
$Topology_Configuration->asXML('saddening.xml');
}
When it is executed for the first time, the file is created(in if part). Otherwise else part is executed. It cannot add the child and is throwing the error in line :
$flavor_name=$flavor->addChild($_POST['arguments']['flavorNa‌​me']);. Please help!!
The XML from your first run results in an XML like this:
<?xml version="1.0"?>
<xml>
<Topology_Configuration>
<Flavor/>
<Networks/>
<VMs/><VNFs/>
</Topology_Configuration>
</xml>
So if you strip down the problem you can reproduce it with:
$Topology_Configuration = simplexml_load_file($fileName);
$flavor=$Topology_Configuration->Flavor;
$flavor->addChild('abc');
echo $Topology_Configuration->asXml();
Results in:
Warning: SimpleXMLElement::addChild(): Cannot add child.
Parent is not a permanent member of the XML tree in
The message is a little wrong, you just try to add the element to an element that does not exists. $Topology_Configuration contains the xml element node, not the Topology_Configuration.
Here are two possible solutions:
Change the XML structure
Create the XML with the Topology_Configuration as the root element.
$topology =new SimpleXMLElement("<Topology_Configuration/>");
Change the access to the Flavor
$xml = simplexml_load_file($fileName);
$flavor=$xml->Topology_Configuration->Flavor;
$flavor->addChild('abc');
At the first time, you can use example to add child nodes
$new_xml = new SimpleXMLElement("<root></root>");
$new_xml->addAttribute('newAttr', 'value');
$newsIntro = $new_xml->addChild('content');
$newsIntro->addAttribute('type', 'value');
Header('Content-type: text/xml');
echo $new_xml->asXML();
and result
<?xml version="1.0"?>
<news newAttr="value">
<content type="value"/>
</news

Get all children from certain xml child element using SimpleXMLElement and xpath

I have xml like:
<root xmlns="urn:test:apis:baseComponents">
<books>
<book>
<name>50 shades of grey</name>
</book>
</books>
<disks>
<disk>
<name>Britney Spears</name>
</disk>
</disks>
</root>
And such php code:
$xml = new SimpleXMLElement($xml);
$books = $xml->books;
$disks = $xml->disks;
$disks->registerXPathNamespace('x', 'urn:test:apis:baseComponents');
$books->registerXPathNamespace('x', 'urn:test:apis:baseComponents');
$b_names = $books->xpath('//x:name');
b_names contains array with 2 values instead of 1. First holds books->book->name, second holds disks->disk->name.
Can you please explain what am I doing wrong and how could I find children of only one element?
The reason that I am using xpath instead of taking manually values using SimpleXMLElement, is that I don't know what value, which I want to search in advance.
Use $books->xpath('.//x:name') to search descendants of your $books variable and not descendants of the root node/document node (which the path //x:name does).

How to parse xml namespaces with DOMDocument and be sure to get objects

I'm trying to parse a xml response from other server.
I can get my needed objects from that xml. but some times and some how, I cant get some objects. and this error appears.
Fatal error: Call to a member function getElementsByTagName() on a non-object in line 91
I checked every thing and I think there is nothing wrong.
here is an example xml response:
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:domain="http://epp.nic.ir/ns/domain-1.0">
<response xmlns:domain="http://epp.nic.ir/ns/domain-1.0">
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData xmlns:domain="http://epp.nic.ir/ns/domain-1.0">
<domain:infData xmlns:domain="http://epp.nic.ir/ns/domain-1.0">
<domain:name>pooyaos.ir</domain:name>
<domain:roid>305567</domain:roid>
<domain:status s="serverHold"/>
<domain:status s="irnicReserved"/>
<domain:status s="serverRenewProhibited"/>
<domain:status s="serverDeleteProhibited"/>
<domain:status s="irnicRegistrationDocRequired"/>
<domain:contact type="holder">pe59-irnic</domain:contact>
<domain:contact type="admin">pe59-irnic</domain:contact>
.
.
and more...
and I am trying to get this object domain:infData
I think the error is from this part.
when I am trying to get this object, domdocument returns null.
php code:
function DinfData()
{
$data = $this->dom->getElementsByTagName("infData")->item(0);
91: $name = $data->getElementsByTagName("name")->item(0)->nodeValue;
$roid = $data->getElementsByTagName("roid")->item(0)->nodeValue;
$update = $data->getElementsByTagName("upDate")->item(0)->nodeValue;
$crdate = $data->getElementsByTagName("crDate")->item(0)->nodeValue;
$exdate = $data->getElementsByTagName("exDate")->item(0)->nodeValue;
and more...
I marked line 91 in error.
thanks ....
edit
$this->dom is my DOMDocument object and has no error.
If nothing is wrong is there any better way to get elements?
You need to use DOMDocument::getElementsByTagNameNS() which is for use with namespaced elements. Find the namespace that domain points to and pass it to the method along with the target tag name e.g. roid or name.
Codepad Demo
$dom = new DOMDocument();
$dom->loadXML($xml);
$ns = 'http://epp.nic.ir/ns/domain-1.0';
$data = $dom->getElementsByTagNameNS($ns, 'infData')->item(0);
$roid = $data->getElementsByTagNameNS($ns, 'roid')->item(0)->nodeValue;
$name = $data->getElementsByTagNameNS($ns, 'name')->item(0)->nodeValue;
// repeat the same for others
echo $roid . "\n";
echo $name;
Outputs
305567
pooyaos.ir
Simply put in your url into simple_load_file()
<?php
$xml = simplexml_load_file($xmlfile);
print $xml->infData; // Sample content
?>
This is a easiest method
But infData is not your tagName. This XML uses namespaces (domain is your namespace). Don't have experience with that so I really can help you any futher then the hint where to look. The getElementsByTagName is not going to work, you need something like DOMXPath I think that's where you can do something with namespaces.
Sorry I can't be of futher help but since no one mentioned the namespaces I thought I would just add the answer anyway. Maybe it will help you Google for an answer.

Using DOMXml and Xpath, to update XML entries

Hello I know there is many questions here about those three topics combined together to update XML entries, but it seems everyone is very specific to a given problem.
I have been spending some time trying to understand XPath and its way, but I still can't get what I need to do.
Here we go
I have this XML file
<?xml version="1.0" encoding="UTF-8"?>
<storagehouse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<item id="c7278e33ef0f4aff88da10dfeeaaae7a">
<name>HDMI Cable 3m</name>
<weight>0.5</weight>
<category>Cables</category>
<location>B3</location>
</item>
<item id="df799fb47bc1e13f3e1c8b04ebd16a96">
<name>Dell U2410</name>
<weight>2.5</weight>
<category>Monitors</category>
<location>C2</location>
</item>
</storagehouse>
What I would like to do is to update/edit any of the nodes above when I need to. I will do a Html form for that.
But my biggest conserne is how do I find and update a the desired node and update it?
Here I have some of what I am trying to do
<?php
function fnDOMEditElementCond()
{
$dom = new DOMDocument();
$dom->load('storage.xml');
$library = $dom->documentElement;
$xpath = new DOMXPath($dom);
// I kind of understand this one here
$result = $xpath->query('/storagehouse/item[1]/name');
//This one not so much
$result->item(0)->nodeValue .= ' Series';
// This will remove the CDATA property of the element.
//To retain it, delete this element (see delete eg) & recreate it with CDATA (see create xml eg).
//2nd Way
//$result = $xpath->query('/library/book[author="J.R.R.Tolkein"]');
// $result->item(0)->getElementsByTagName('title')->item(0)->nodeValue .= ' Series';
header("Content-type: text/xml");
echo $dom->saveXML();
}
?>
Could someone maybe give me an examples with attributes and so on, so one a user decides to update a desired node, I could find that node with XPath and then update it?
The following example is making use of simplexml which is a close friend of DOMDocument. The xpath shown is the same regardless which method you use, and I use simplexml here to keep the code low. I'll show a more advanced DOMDocument example later on.
So about the xpath: How to find the node and update it. First of all how to find the node:
The node has the element/tagname item. You are looking for it inside the storagehouse element, which is the root element of your XML document. All item elements in your document are expressed like this in xpath:
/storagehouse/item
From the root, first storagehouse, then item. Divided with /. You already know that, so the interesting part is how to only take those item elements that have the specific ID. For that the predicate is used and added at the end:
/storagehouse/item[#id="id"]
This will return all item elements again, but this time only those which have the attribute id with the value id (string). For example in your case with the following XML:
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<storagehouse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="schema.xsd">
<item id="c7278e33ef0f4aff88da10dfeeaaae7a">
<name>HDMI Cable 3m</name>
<weight>0.5</weight>
<category>Cables</category>
<location>B3</location>
</item>
<item id="df799fb47bc1e13f3e1c8b04ebd16a96">
<name>Dell U2410</name>
<weight>2.5</weight>
<category>Monitors</category>
<location>C2</location>
</item>
</storagehouse>
XML;
that xpath:
/storagehouse/item[#id="df799fb47bc1e13f3e1c8b04ebd16a96"]
will return the computer monitor (because such an item with that id exists). If there would be multiple items with the same id value, multiple would be returned. If there were none, none would be returned. So let's wrap that into a code-example:
$simplexml = simplexml_load_string($xml);
$result = $simplexml->xpath(sprintf('/storagehouse/item[#id="%s"]', $id));
if (!$result || count($result) !== 1) {
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id));
}
list($item) = $result;
In this example, $titem is the SimpleXMLElement object of that computer monitor xml element name item.
So now for the changes, which are extremely easy with SimpleXML in your case:
$item->category = 'LCD Monitor';
And to finally see the result:
echo $simplexml->asXML();
Yes that's all with SimpleXML in your case.
If you want to do this with DOMDocument, it works quite similar. However, for updating an element's value, you need to access the child element of that item as well. Let's see the following example which first of all fetches the item as well. If you compare with the SimpleXML example above, you can see that things not really differ:
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$result = $xpath->query(sprintf('/storagehouse/item[#id="%s"]', $id));
if (!$result || $result->length !== 1) {
throw new Exception(sprintf('Item with id "%s" does not exists or is not unique.', $id));
}
$item = $result->item(0);
Again, $item contains the item XML element of the computer monitor. But this time as a DOMElement. To modify the category element in there (or more precisely it's nodeValue), that children needs to be obtained first. You can do this again with xpath, but this time with an expression relative to the $item element:
./category
Assuming that there always is a category child-element in the item element, this could be written as such:
$category = $xpath->query('./category', $item)->item(0);
$category does now contain the first category child element of $item. What's left is updating the value of it:
$category->nodeValue = "LCD Monitor";
And to finally see the result:
echo $doc->saveXML();
And that's it. Whether you choose SimpleXML or DOMDocument, that depends on your needs. You can even switch between both. You probably might want to map and check for changes:
$repository = new Repository($xml);
$item = $repository->getItemByID($id);
$item->category = 'LCD Monitor';
$repository->saveChanges();
echo $repository->getXML();
Naturally this requires more code, which is too much for this answer.

Categories