xml:lang parse in PHP - php

<?xml version="1.0" encoding="UTF-8"?>
<answer>
<describe data="aircompany">
<data>
<code xml:lang="ru">FW</code>
<code xml:lang="en">FW</code>
</data>
<data>
<code xml:lang="ru">UT</code>
<code xml:lang="en">ЮТ</code>
</data>
</describe>
</answer>
I need get nodes value, there xml:lang="en". How can do it in PHP?

Yes, SimpleXML works but try adding the xml namespace if you run into trouble.
E.g.:
<?php
$xmlstr = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<answer xmlns:xml="http://www.w3.org/XML/1998/namespace">
<describe data="aircompany">
<data>
<code xml:lang="ru">ФВ</code>
<code xml:lang="en">FW</code>
</data>
<data>
<code xml:lang="ru">УТ</code>
<code xml:lang="en">UT</code>
</data>
</describe>
</answer>
XML;
$xml = new SimpleXMLElement($xmlstr);
foreach ($xml->xpath('//data/code[#xml:lang="en"]') as $code) {
echo $code, '<br/>', PHP_EOL;
}
?>

XPath has a special construct for dealing with xml:lang attribute:
$xml = new SimpleXMLElement($strXML);
$data = $xml->describe->data[0];
$elCode = $data->xpath("code[lang('en')]"); // returns array of SimpleXMLElement
assert(count($elCode)==1);
$code_en = (string) $elCode[0];
P.S. greetings to the Sirena ;)

Thanks.
I use this method:
$XMLObj = new SimpleXMLElement($XML);
print_r($XMLObj->xpath('/answer/describe/data/code[#xml:lang = "en"]'));

Has been answered before:
$dom =new DOMDocument;
$dom->loadXML( $xml );
$xPath = new DOMXPath( $dom );
$nodes = $xPath->query( '/answer/describe/data/code[#xml:lang = "en"]' );
foreach( $nodes as $node ) {
echo $node->nodeValue;
}
Alternative without XPath:
$dom =new DOMDocument;
$dom->loadXML ($xml );
foreach( $dom->getElementsByTagName( 'code' ) as $node ) {
if( $node->getAttribute( 'xml:lang' ) === 'en') {
echo $node->nodeValue;
}
}

Have a look at the PHP SAX parser decribed here.

Related

Simlpexml and registerxpathnamespace issue with soap response body

Here is the xml: (a.xml)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://www.example.com/v1/services">
<soapenv:Header/>
<soapenv:Body>
<ser:getAnalyticalDeliveryEstimatesRequest>
<ser:buyer>
<ser:buyerId>1233</ser:buyerId>
<ser:toCountry>IN</ser:toCountry>
<ser:toZip>110001</ser:toZip>
</ser:buyer>
<ser:item>
<ser:id>25164</ser:id>
<ser:categoryId>15032</ser:categoryId>
<ser:seller>
<ser:sellerId>11997</ser:sellerId>
<ser:fromCountry>IN</ser:fromCountry>
</ser:seller>
<ser:transactionId>0</ser:transactionId>
</ser:item>
</ser:getAnalyticalDeliveryEstimatesRequest>
</soapenv:Body>
</soapenv:Envelope>
The PHP code to parse this:
$xml = simplexml_load_file( 'a.xml', NULL, NULL, 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('ser', 'http://www.example.com/v1/services');
$xpath = $xml->xpath( '//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest' );
print_r($xpath);
It gives no data.
Please let me know if i am doing it wrong.
Firstly, in your example code, you haven't actually defined $node anywhere:
$xpath = $xml->xpath( '//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest' );
print_r($node);
Should be:
$xpath = $xml->xpath( '//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest' );
$node = $xpath[0];
print_r($node);
Or perhaps:
$xpath = $xml->xpath( '//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest' );
foreach ( $xpath as $node ) {
print_r($node);
}
Secondly, print_r is not great at displaying SimpleXML objects. It giving you an empty output doesn't mean that the element is empty.
For instance, try echoing the name of the node found (Demo):
$xpath = $xml->xpath( '//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest' );
foreach ( $xpath as $node ) {
echo $node->getName();
}
To get at its contents, you need to select the namespace they are in, with the ->children() method, at which point even print_r will be able to see them (though there are other reasons not to rely on it entirely) (Demo):
$xpath = $xml->xpath( '//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest' );
foreach ( $xpath as $node ) {
print_r( $node->children('http://www.example.com/v1/services') );
}
Try searching for "SimpleXML with namespaces" for more examples.
It's quite odd to see thats not working, but alternatively you could also use this:
$dom = new DOMDocument();
$dom->loadXML($xml);
$xpath = new DOMXpath($dom);
$element = $xpath->query('//soapenv:Body/ser:getAnalyticalDeliveryEstimatesRequest');
foreach($element->item(0)->childNodes as $node) {
// perform your actions here
}
Sample Output
Edit: Also this is another way:
$xml_string ='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ser="http://www.example.com/v1/services">
<soapenv:Header/>
<soapenv:Body>
<ser:getAnalyticalDeliveryEstimatesRequest>
<ser:buyer>
<ser:buyerId>1233</ser:buyerId>
<ser:toCountry>IN</ser:toCountry>
<ser:toZip>110001</ser:toZip>
</ser:buyer>
<ser:item>
<ser:id>25164</ser:id>
<ser:categoryId>15032</ser:categoryId>
<ser:seller>
<ser:sellerId>11997</ser:sellerId>
<ser:fromCountry>IN</ser:fromCountry>
</ser:seller>
<ser:transactionId>0</ser:transactionId>
</ser:item>
</ser:getAnalyticalDeliveryEstimatesRequest>
</soapenv:Body>
</soapenv:Envelope>';
$xml = simplexml_load_string($xml_string, null, null, 'http://schemas.xmlsoap.org/soap/envelope/');
$ns = $xml->getNamespaces(true);
$soap = $xml->children($ns['soapenv']);
foreach($soap->Body as $nodes) {
$ser = $nodes->children($ns['ser'])->getAnalyticalDeliveryEstimatesRequest;
foreach($ser->buyer as $sub_nodes) { // this can also be ->item as well
}
}

Replace HTML tags with DOMdocument while keeping nested tags [duplicate]

I'm trying to figure out how to rename a node in XML using PHP?
I Have come this far:
$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// load the xml file
$dom->loadXML( '<?xml version="1.0" encoding="ISO-8859-1"?>
<library>
<data>
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
//find all 'data' nodes.
$node = $xpath->query("//data");
// if found
if( $node->length ) {
foreach ($node as $n) {
// RENAME HERE? //
}
}
echo "<xmp>". $dom->saveXML() ."</xmp>";
Now, I want to rename <data> to <invites>. Can this be done and if yes, how?
A Node's name ("data" or "invites" respectively) cannot be renamed via the DOM because the Node::nodeName property is read-only.
You can create a new node named "invites", append it before the "data" node, move the children of "data" to the new "invites" node, remove the "data" node, and then output the tree to get your result.
Example:
<?php
// Create a test document.
$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// Load the xml file.
$dom->loadXML('<?xml version="1.0" encoding="ISO-8859-1"?'.'>
<library>
<data attr1="1" attr2="2">
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
// Convert <data> to <invites>.
if ($dataNode = $xpath->query("/library/data")->item(0))
{
// Create the <invites> node.
$invitesNode = $dom->createElement('invites');
$dataAttrs = $dataNode->attributes;
foreach ($dataAttrs as $dataAttr)
{ $invitesNode->setAttributeNodeNS($dataAttr->cloneNode()); }
$dom->documentElement->appendChild($invitesNode);
// Move the <data> children over.
if ($childNodes = $xpath->query("/library/data/*"))
{
foreach ($childNodes as $childNode)
{ $invitesNode->appendChild($childNode); }
}
// Remove <data>.
$dataNode->parentNode->removeChild($dataNode);
}
// Test the result.
echo $dom->saveXML();
?>
My solution, with extended test case:
// Changes the name of element $element to $newName.
function renameElement($element, $newName) {
$newElement = $element->ownerDocument->createElement($newName);
$parentElement = $element->parentNode;
$parentElement->insertBefore($newElement, $element);
$childNodes = $element->childNodes;
while ($childNodes->length > 0) {
$newElement->appendChild($childNodes->item(0));
}
$attributes = $element->attributes;
while ($attributes->length > 0) {
$attribute = $attributes->item(0);
if (!is_null($attribute->namespaceURI)) {
$newElement->setAttributeNS('http://www.w3.org/2000/xmlns/',
'xmlns:'.$attribute->prefix,
$attribute->namespaceURI);
}
$newElement->setAttributeNode($attribute);
}
$parentElement->removeChild($element);
}
function prettyPrint($d) {
$d->formatOutput = true;
echo '<pre>'.htmlspecialchars($d->saveXML()).'</pre>';
}
$d = new DOMDocument( '1.0' );
$d->loadXML('<?xml version="1.0"?>
<library>
<data a:foo="1" x="bar" xmlns:a="http://example.com/a">
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>');
$xpath = new DOMXPath($d);
$elements = $xpath->query('/library/data');
if ($elements->length == 1) {
$element = $elements->item(0);
renameElement($element, 'invites');
}
prettyPrint($d);
By the way, I added this solution as a comment to the PHP documentation for DOMElement.

Rename an XML node using PHP

I'm trying to figure out how to rename a node in XML using PHP?
I Have come this far:
$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// load the xml file
$dom->loadXML( '<?xml version="1.0" encoding="ISO-8859-1"?>
<library>
<data>
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
//find all 'data' nodes.
$node = $xpath->query("//data");
// if found
if( $node->length ) {
foreach ($node as $n) {
// RENAME HERE? //
}
}
echo "<xmp>". $dom->saveXML() ."</xmp>";
Now, I want to rename <data> to <invites>. Can this be done and if yes, how?
A Node's name ("data" or "invites" respectively) cannot be renamed via the DOM because the Node::nodeName property is read-only.
You can create a new node named "invites", append it before the "data" node, move the children of "data" to the new "invites" node, remove the "data" node, and then output the tree to get your result.
Example:
<?php
// Create a test document.
$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// Load the xml file.
$dom->loadXML('<?xml version="1.0" encoding="ISO-8859-1"?'.'>
<library>
<data attr1="1" attr2="2">
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
// Convert <data> to <invites>.
if ($dataNode = $xpath->query("/library/data")->item(0))
{
// Create the <invites> node.
$invitesNode = $dom->createElement('invites');
$dataAttrs = $dataNode->attributes;
foreach ($dataAttrs as $dataAttr)
{ $invitesNode->setAttributeNodeNS($dataAttr->cloneNode()); }
$dom->documentElement->appendChild($invitesNode);
// Move the <data> children over.
if ($childNodes = $xpath->query("/library/data/*"))
{
foreach ($childNodes as $childNode)
{ $invitesNode->appendChild($childNode); }
}
// Remove <data>.
$dataNode->parentNode->removeChild($dataNode);
}
// Test the result.
echo $dom->saveXML();
?>
My solution, with extended test case:
// Changes the name of element $element to $newName.
function renameElement($element, $newName) {
$newElement = $element->ownerDocument->createElement($newName);
$parentElement = $element->parentNode;
$parentElement->insertBefore($newElement, $element);
$childNodes = $element->childNodes;
while ($childNodes->length > 0) {
$newElement->appendChild($childNodes->item(0));
}
$attributes = $element->attributes;
while ($attributes->length > 0) {
$attribute = $attributes->item(0);
if (!is_null($attribute->namespaceURI)) {
$newElement->setAttributeNS('http://www.w3.org/2000/xmlns/',
'xmlns:'.$attribute->prefix,
$attribute->namespaceURI);
}
$newElement->setAttributeNode($attribute);
}
$parentElement->removeChild($element);
}
function prettyPrint($d) {
$d->formatOutput = true;
echo '<pre>'.htmlspecialchars($d->saveXML()).'</pre>';
}
$d = new DOMDocument( '1.0' );
$d->loadXML('<?xml version="1.0"?>
<library>
<data a:foo="1" x="bar" xmlns:a="http://example.com/a">
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>');
$xpath = new DOMXPath($d);
$elements = $xpath->query('/library/data');
if ($elements->length == 1) {
$element = $elements->item(0);
renameElement($element, 'invites');
}
prettyPrint($d);
By the way, I added this solution as a comment to the PHP documentation for DOMElement.

php domdocument get node value where attribute value is

Say my XML looks like this:
<record>
<row name="title">this item</row>
<row name="url">this url</row>
</record>
Now I'm doing something like this:
$xml = new DOMDocument();
$xml->load('xmlfile.xml');
echo $xml->getElementByTagName('row')->item(0)->attributes->getNamedItem('title')->nodeValue;
But this just gives me:
NOTICE: Trying to get property of non-object id
Does anybody know how to get the node value where the "name" attribute has value "title"?
Try:
$xml = new DOMDocument();
$xml->loadXml('
<record>
<row name="title">this item</row>
<row name="url">this url</row>
</record>
');
$xpath = new DomXpath($xml);
// traverse all results
foreach ($xpath->query('//row[#name="title"]') as $rowNode) {
echo $rowNode->nodeValue; // will be 'this item'
}
// Or access the first result directly
$rowNode = $xpath->query('//row[#name="title"][1]')->item(0);
if ($rowNode instanceof DomElement) {
echo $rowNode->nodeValue;
}
foreach ($xml->getElementsByTagName('row') as $element)
{
if ($element->getAttribute('name') == "title")
{
echo $element->nodeValue;
}
}
$xpath = new DOMXPath( $xml );
$val = $xpath->query( '//row[#name="title"]' )->item(0)->nodeValue;

How do extract child element in XML using DOM in PHP 5.0?

I am having the XML like this
<?xml version="1.0" encoding="utf-8"?>
<root>
<mynode catid="10" catname="Animals" label="Animals" catdesc="" parent_id="2">
<mynode catid="11" catname="Lions" label="Lions" catdesc="" parent_id="10">
<mynode catid="12" catname="lion" label="lion" catdesc="" parent_id="11"/>
<mynode catid="13" catname="lioness" label="lioness" catdesc="" parent_id="11"/>
</mynode>
</mynode>
</root>
From this I want to remove
<?xml version="1.0" encoding="utf-8"?>
<root>
and
</root>
So expected result is
<mynode catid="10" catname="Animals" label="Animals" catdesc="" parent_id="2">
<mynode catid="11" catname="Lions" label="Lions" catdesc="" parent_id="10">
<mynode catid="12" catname="lion" label="lion" catdesc="" parent_id="11"/>
<mynode catid="13" catname="lioness" label="lioness" catdesc="" parent_id="11"/>
</mynode>
</mynode>
How can I do this?
Edit 1:TO Phil
$dom = new DomDocument();
//$dom->preserveWhitespace = false;
$dom->load('treewithchild.xml');
function DOMinnerHTML($element)
{
$innerHTML = "";
$children = $element->childNodes;
foreach ($children as $child)
{
$tmp_dom = new DOMDocument();
$tmp_dom->appendChild($tmp_dom->importNode($child, true));
$innerHTML.=trim($tmp_dom->saveXML());
echo $tmp_dom->saveXML();
}
return $innerHTML;
}
$dom->preserveWhiteSpace = false;
$domTable = $dom->getElementsByTagName("mynode");
foreach ($domTable as $tables)
{
//echo $tables;
DOMinnerHTML($tables);
}
As you want the inner markup of the <root> node, that is the element who's child nodes you'll want to iterate. You can access this element using the DOMDocument::documentElement property.
Try this (tested and working)
$doc = new DOMDocument;
$doc->load('treewithchild.xml');
$inner = '';
foreach ($doc->documentElement->childNodes as $child) {
$inner .= $doc->saveXML($child);
}
echo $inner;
I expect that the root element is returned also, you have to know that for each xml file an is added impliicitly, even if it exists in your file. so try to do this
$children = $element->childNodes->childNodes;
i think that would help you.

Categories