PHP - Finding and removing an XML node via DOM - php

Can anyone please point out why the PHP below isn't deleting the XML node?
I'm wanting to delete the entire image node that contains the original_number value of 823.
Thanks in advance.
$xml = '<?xml version="1.0"?>
<property>
<edited>true</edited>
<images>
<image>
<is_required><![CDATA[true]]></is_required>
<original_number><![CDATA[823]]></original_number>
</image>
<image>
<is_required><![CDATA[true]]></is_required>
<original_number><![CDATA[555]]></original_number>
</image>
</images>
</property>';
$originalNumberToDelete = 823;
$dom = new DOMDocument();
$dom->loadXML( $xml->asXML(), LIBXML_NOBLANKS );
$dom->formatOutput = true;
$xpath = new DOMXPath($dom);
foreach( $xpath->query("property/images/image[original_number='".$originalNumberToDelete."']") as $node) {
$node->parentNode->removeChild($node);
}
echo $dom->saveXML();

Maybe a possible solution is to change
$dom->loadXML( $xml->asXML(), LIBXML_NOBLANKS );
to
$dom->loadXML( $xml, LIBXML_NOBLANKS );
Then start your xpath expression with a double forward slash:
$xml = '<?xml version="1.0"?>
<property>
<edited>true</edited>
<images>
<image>
<is_required><![CDATA[true]]></is_required>
<original_number><![CDATA[823]]></original_number>
</image>
<image>
<is_required><![CDATA[true]]></is_required>
<original_number><![CDATA[555]]></original_number>
</image>
</images>
</property>';
$originalNumberToDelete = 823;
$dom = new DOMDocument();
$dom->loadXML( $xml, LIBXML_NOBLANKS );
$dom->formatOutput = true;
$xpath = new DOMXPath($dom);
foreach( $xpath->query("//property/images/image[original_number='".$originalNumberToDelete."']") as $node) {
$node->parentNode->removeChild($node);
}
echo $dom->saveXML();
Demo

Related

PHP Xpath return zero match

Considering this XML
<?xml version="1.0" encoding="UTF-8"?>
<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd" ID="_a75adf55-01d7-40cc-929f-dbd8372ebdfc" IssueInstant="2009-09-09T00:46:02Z" Version="2.0">
<Subject>
<NameID>801234567890</NameID>
</Subject>
....
</Assertion>
PHP
$dom = new DOMDocument();
$ret = $dom->loadXML($data);
$xp = new DOMXPath($dom);
$node_list = $xp->query('/Assertion');
$node_list->length return 0 element. I want to extract the DOMElement but somehow it didn't work.
As stated on the comments by Sami Kuhmonen you may need to register the namespaces, here is an example:
<?php
$string= <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd" ID="_a75adf55-01d7-40cc-929f-dbd8372ebdfc" IssueInstant="2009-09-09T00:46:02Z" Version="2.0">
<Subject>
<NameID>801234567890</NameID>
</Subject>
</Assertion>
XML;
$dom = new DOMDocument();
$dom->loadXML($string);
$xp = new DOMXPath($dom);
// registering the namespaces
$xp->registerNamespace('a','urn:oasis:names:tc:SAML:2.0:assertion');
$xp->registerNamespace('b','http://www.w3.org/2001/XMLSchema-instance');
// using the prefix of the registered namespace in the xpath expression
$node_list = $xp->query('/a:Assertion');
print $node_list->length
?>

XML edit in php

In an xml file I have this structure (with <post>repeating) :
<data>
<post>
<deal_id>479</deal_id>
<deal_title><![CDATA[Δίπλωμα Μηχανής, στη Σχολή Οδηγών Παραστατίδης στον Εύοσμο, μόνο με 49€]]></deal_title>
<deal_url>http://domain.com/site/shop/autokinito-el/diplwma-mixanis-sxoli-odigwn-parastatidis-euosmos/</deal_url>
<deal_city><![CDATA[Θεσσαλονίκη]]></deal_city>
<deal_price><![CDATA[49]]></deal_price>
<deal_previous_price><![CDATA[125]]></deal_previous_price>
<deal_discount><![CDATA[60.8]]></deal_discount>
<deal_start><![CDATA[2016-01-10 00:00:00]]></deal_start>
<deal_end><![CDATA[2016-04-01 00:00:00]]></deal_end>
<deal_image>
<image>
<file>http://domain.com/site/wp-content/uploads/2015/09/c700x420.jpg</file>
<title>c700x420</title>
<caption></caption>
<description></description>
<alt></alt>
</image>
<image>
<file>http://domain.com/site/wp-content/uploads/2015/09/diploma1.jpg</file>
<title>diploma1</title>
<caption></caption>
<description></description>
<alt></alt>
</image>
</deal_image>
<deal_sales><![CDATA[0]]></deal_sales>
<deal_active><![CDATA[true]]></deal_active></post></data>
and I would like to transform the <deal_image> to
<deal_image>http://domain.com/site/wp-content/uploads/2015/09/c700x420.jpg</deal_image>
which means keep only the first appearing jpg and discard the rest..and do that for all <post>
How can I do that with php?
So the desired output would be something like this:
<data>
<post>
<deal_id>479</deal_id>
<deal_title><![CDATA[Δίπλωμα Μηχανής, στη Σχολή Οδηγών Παραστατίδης στον Εύοσμο, μόνο με 49€]]></deal_title>
<deal_url>http://domain.com/site/shop/autokinito-el/diplwma-mixanis-sxoli-odigwn-parastatidis-euosmos/</deal_url>
<deal_city><![CDATA[Θεσσαλονίκη]]></deal_city>
<deal_price><![CDATA[49]]></deal_price>
<deal_previous_price><![CDATA[125]]></deal_previous_price>
<deal_discount><![CDATA[60.8]]></deal_discount>
<deal_start><![CDATA[2016-01-10 00:00:00]]></deal_start>
<deal_end><![CDATA[2016-04-01 00:00:00]]></deal_end>
<deal_image>http://domain.com/site/wp-content/uploads/2015/09/c700x420.jpg</deal_image>
<deal_sales><![CDATA[0]]></deal_sales>
<deal_active><![CDATA[true]]></deal_active></post></data>
Note that the deal_image tag kept only the url of the first image and ignored the rest. In my xml file there are lots of <post></post> sections, which should also be processed in an iteration.
First we remove all image childs of deal_image, then we assign the image value to deal_image
<?php
$dom = new DOMDocument();
$dom->formatOutput = true;
$dom->loadXML('<data>
<post>
<deal_id>479</deal_id>
<deal_title><![CDATA[Δίπλωμα Μηχανής, στη Σχολή Οδηγών Παραστατίδης στον Εύοσμο, μόνο με 49€]]></deal_title>
<deal_url>http://domain.com/site/shop/autokinito-el/diplwma-mixanis-sxoli-odigwn-parastatidis-euosmos/</deal_url>
<deal_city><![CDATA[Θεσσαλονίκη]]></deal_city>
<deal_price><![CDATA[49]]></deal_price>
<deal_previous_price><![CDATA[125]]></deal_previous_price>
<deal_discount><![CDATA[60.8]]></deal_discount>
<deal_start><![CDATA[2016-01-10 00:00:00]]></deal_start>
<deal_end><![CDATA[2016-04-01 00:00:00]]></deal_end>
<deal_image>
<image>
<file>http://domain.com/site/wp-content/uploads/2015/09/c700x420.jpg</file>
<title>c700x420</title>
<caption></caption>
<description></description>
<alt></alt>
</image>
<image>
<file>http://domain.com/site/wp-content/uploads/2015/09/diploma1.jpg</file>
<title>diploma1</title>
<caption></caption>
<description></description>
<alt></alt>
</image>
</deal_image>
<deal_sales><![CDATA[0]]></deal_sales>
<deal_active><![CDATA[true]]></deal_active></post></data>');
$featuredde1 = $dom->getElementsByTagName('image');
foreach ($featuredde1 as $node) {
$node->parentNode->removeChild($node);
}
foreach ($featuredde1 as $node) {
$node->parentNode->removeChild($node);
}
$data = $dom->getElementsByTagName( "deal_image" );
$data ->item(0)->nodeValue = "http://domain.com/site/wp-content/uploads/2015/09/c700x420.jpg";
echo $dom->saveXML();
DEMO
http://ideone.com/HlrKM4
You can use DOMDocument with DOMXPath:
$dom = new DOMDocument();
$dom->loadXML( $xml, LIBXML_NOBLANKS );
$xpath = new DOMXPath( $dom );
foreach( $xpath->query( '/data/post/deal_image' ) as $deal_image )
{
$value = $xpath->query( './image/file', $deal_image )->item(0)->nodeValue;
$new_node = $dom->createElement( 'deal_image', $value );
$deal_image->parentNode->replaceChild( $new_node, $deal_image );
}
$dom->formatOutput = True;
echo $dom->saveXML().PHP_EOL;
($xml is your XML string; if you want load XML directly from a file, you can use $dom->load( $filePath ) instead of $dom->loadXML( $xml ) )
After init of DOMDocument and DOMXPath, the foreach loop examine all <deal_image> nodes, find the value of first <image><file>, create a new <deal_image> with this found value and replace old <deal_image> with the new created.
In the demo above I have added two more <post> to test the behavior with 1 or 3 images.
See more about DOMDocument
See more about DOMXPath

PHP XML: how to use createCDataSection() properly

I am trying to create an xml dynamically using domDocument.
What I would like to obtain is following:
<?xml version="1.0"?>
<books>
<book>
<content>
<name><![CDATA[dddd]]></name>
</content>
</book>
</books>
Unfortunatelly the script below does not show the output as excepted.
$xml = new DOMDocument('1.0');
$xml->formatOutput = true;
$books = $xml->createElement('books');
$xml->appendChild($books);
$book = $xml->createElement('book');
$books->appendChild($book);
$inside = $xml->createElement('content');
$book->appendChild($inside);
$xml->appendChild($inside)->appendChild($xml->createElement('name'))->appendChild($xml->createCDataSection('dddd'));
echo '<xmp>'.$xml->saveXML().'</xmp>';
This is the output
<?xml version="1.0"?>
<books>
<book>
<content>
<name><![CDATA[dddd]]></name>
<lastname><![CDATA[dddd]]></lastname>
....
</content>
I do not know how to use createCDataSection().
Like this?
$xml = new DOMDocument('1.0');
$xml->formatOutput = true;
$books = $xml->createElement('books');
$xml->appendChild($books);
$book = $xml->createElement('book');
$books->appendChild($book);
$content = $xml->createElement('content');
$book->appendChild($content);
$name = $xml->createElement('name');
$content->appendChild($name);
$name->appendChild($xml->createCDataSection('dddd'));
echo '<xmp>'.$xml->saveXML().'</xmp>';

Missing formatting after adding childnode?

I'm trying to add a childnode in an XML document with PHP and got it OK so far except one thing. Can't get it formatted correct?
Here is the script:
$xmldoc = new DOMDocument();
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xmldoc->loadXML('<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<udate>1347730639</udate>
<userid>3</userid>
</data>');
echo "<xmp>OLD:\n". $xmldoc->saveXML() ."</xmp>";
$root = $xmldoc->firstChild;
$newElement = $xmldoc->createElement('popup');
$root->appendChild($newElement);
$newText = $xmldoc->createTextNode("0");
$newElement->appendChild($newText);
echo "<xmp>NEW:\n". $xmldoc->saveXML() ."</xmp>";
After adding the node I get this:
<data>
<udate>1347730639</udate>
<userid>3</userid>
<popup>0</popup></data>
I want it to be like this:
<data>
<udate>1347730639</udate>
<userid>3</userid>
<popup>0</popup>
</data>
Where do I go wrong?
Please help and thanks in advance :-)
createElement will break formatOutput this is a general issue
See PHP BUG Report
formatOutput does not work with saveHTML
DOMDocument->formatOutput = true does not work
But you can have a work around by reloading and formatting it.
$xmldoc = new DOMDocument();
$xmldoc->loadXML('<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<udate>1347730639</udate>
<userid>3</userid>
</data>');
echo "<xmp>OLD:\n". $xmldoc->saveXML() ."</xmp>";
$root = $xmldoc->firstChild;
$newElement = $xmldoc->createElement('popup');
$root->appendChild($newElement);
$newText = $xmldoc->createTextNode("0");
$newElement->appendChild($newText);
$xml = new DOMDocument();
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
$xml->loadXML( $xmldoc->saveXML());
echo "<xmp>NEW:\n". $xml->saveXML()."</xmp>";
Output
OLD:
<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<udate>1347730639</udate>
<userid>3</userid>
</data>
NEW:
<?xml version="1.0" encoding="ISO-8859-1"?>
<data>
<udate>1347730639</udate>
<userid>3</userid>
<popup>0</popup>
</data>
You might want to try removing all whitespace before and then formatting.
force it to remake the xml from scratch.
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;

Removing a node from xml file

I have an xml file
<?xml version="1.0"?>
<category>
<name>SWEATERS</name>
<name>WATCHES</name>
<name>PANTS</name>
<name>test</name>
<name>1</name>
</category>
How i can remove the node <name>test</name> using xpath ,xquery and php. I used this code
$name='test;
$xmlfile="config/shop_categories.xml";
$xml = simplexml_load_file($xmlfile);
$target = $xml->xpath('/category[name="'.trim($name).'"]');
print_r($target[0]);
if($target == false)
return;
$domRef = dom_import_simplexml($target[0]); //Select position 0 in XPath array
$domRef->parentNode->removeChild($domRef);
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
$dom->save($xmlfile);
But it is not working.
Pretty sure this is a duplicate, but am too lazy to find it. Here you go:
$xml = <<< XML
<?xml version="1.0"?>
<category>
<name>SWEATERS</name>
<name>WATCHES</name>
<name>PANTS</name>
<name>test</name>
<name>1</name>
</category>
XML;
$dom = new DOMDocument;
$dom->loadXML($xml);
$xPath = new DOMXPath($dom);
foreach($xPath->query('//name[text() = "test"]') as $node) {
$node->parentNode->removeChild($node);
}
echo $dom->saveXML();
Output:
<?xml version="1.0"?>
<category>
<name>SWEATERS</name>
<name>WATCHES</name>
<name>PANTS</name>
<name>1</name>
</category>

Categories