Deleting an xml node and all children via php - php

I have been looking around here and google and found various code, but none of them work. I'm assuming it's because none of them match my exact situation. I have an xml structure like this:
<employees>
<employee EmpID="">
<first_name></first_name>
<last_name></last_name>
<ssnum></ssnum>
<status></status>
<contact_info>
<office_phone></office_phone>
<email></email>
<cell_phone></cell_phone>
</contact_info>
<access_info level="user">
<username></username>
<password></password>
</access_info>
<department></department>
<date_started></date_started>
<years></years>
<position></position>
<salary></salary>
<e_increase></e_increase>
<e_raise></e_raise>
<photo></photo>
</employee>
</employees>
I have the EmpID of the employee I want to delete stored in a variable called $ID. I want to remove that employee completely. I have tried things like:
foreach ($doc->getElementsByTagName('employee') as $employee) {
if($employee->getAttribute('EmpID') === $ID) {
foreach ($employee as $node) {
$node->parentNode->removeChild($node);
}
}
}
and other loops trying to get things similarly, as in these posts: PHP XML remove element and all children by name and Remove all children from a XML Node PHP DOM but I have not been able to get this to work successfully.
Any help would be appreciated, thanks.

$ID = '';
$dom = new DOMDocument();
$dom->loadXML($xml_string);
$xpath = new DOMXpath($dom);
$nodes = $xpath->evaluate('/employees/employee[#EmpID = "' . $ID . '"]');
foreach ($nodes as $node) {
$node->parentNode->removeChild($node);
}

there you go:
$xml = '<employees>
<employee EmpID="1">
<first_name></first_name>
<last_name></last_name>
<ssnum></ssnum>
<status></status>
<contact_info>
<office_phone></office_phone>
<email></email>
<cell_phone></cell_phone>
</contact_info>
<access_info level="user">
<username></username>
<password></password>
</access_info>
<department></department>
<date_started></date_started>
<years></years>
<position></position>
<salary></salary>
<e_increase></e_increase>
<e_raise></e_raise>
<photo></photo>
</employee>
<employee EmpID="2">
<first_name></first_name>
<last_name></last_name>
<ssnum></ssnum>
<status></status>
<contact_info>
<office_phone></office_phone>
<email></email>
<cell_phone></cell_phone>
</contact_info>
<access_info level="user">
<username></username>
<password></password>
</access_info>
<department></department>
<date_started></date_started>
<years></years>
<position></position>
<salary></salary>
<e_increase></e_increase>
<e_raise></e_raise>
<photo></photo>
</employee>
</employees>';
$doc = new DOMDocument();
$doc->loadXML($xml);
$selector = new DOMXPath($doc);
$els = $selector->query('/employees//employee');
//or
//$els = $doc->getElementsByTagName('employee');
foreach($els as $el){
if($el->getAttribute('EmpID') == 1){
$el->parentNode->removeChild($el);
}
}
$xml = $doc->saveXML();
echo($xml);

Looking up my xml I have it slightly different .. Give this a try.
foreach ($doc->getElementsByTagName('employee') as $employee) {
if($employee->getAttribute('EmpID') === $ID) {
$employee->parentNode->removeChild($employee);
// $dom->save($xmlFile);
}
}

Related

How to remove XML parent node based on child conditions PHP

Hi I am trying to clean up xml file out of positions I dont need. Here is my code so far:
<?php
$doc = new DOMDocument;
$doc->load('merg.xml');
$xpath = new DOMXPath($doc);
$products = $xpath->query('//offer/products/*');
printf('There is %d products<br /><br />', $products->length);
function findStopPointByName($xml, $query) {
$upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZĄŻŚĆŹĆÓŁ";
$lower = "abcdefghijklmnopqrstuvwxyzążśćźńół";
$arg_query = "translate('$query', '$upper', '$lower')";
return $xml->query("//offer/products/product/description/name[contains(text(),$arg_query)]");
}
foreach(findStopPointByName($xpath,'Skór') as $node)
{
$node->parentNode->removeChild($node);
}
$doc->save('merg_fixed.xml');
?>
Structure of XML:
<offer>
<products>
<product>
<description>
<name>Name of the product</name>
...
</name>
...
</description>
</product>
</products>
</offer>
I am trying to remove all PRODUCT where its NAME contains 'Skór' in any case (Skór, skór, SKÓR - is enough). Funcion findStopPointByName returns DOMNodeList of correct length, but nothing is removed from actual XML file, please help.
First, you can directly find node product with the condition
Second, to make search case insensitive, you can translate node text in any case but should use pattern in the same case. As the result, your code may be so
function findStopPointByName($xml, $query) {
$upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZĄŻŚĆŹĆÓŁ";
$lower = "abcdefghijklmnopqrstuvwxyzążśćźńół";
$arg_query = "translate(text(), '$upper', '$lower')";
$q = "//product[description/name[contains($arg_query, '$query')]]" ."\n";
return $xml->query($q);
}
$doc = new DOMDocument;
$doc->load('merg.xml');
$xpath = new DOMXPath($doc);
foreach(findStopPointByName($xpath,'skór') as $node)
$node->parentNode->removeChild($node);
echo $doc->saveXML();
Demo on eval.in

PHP SOAP XML DOM

I have a soap xml that contains a bunch of variables that I need to access. Here is the XML.
`<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<searchPersonsResponse xmlns="">
<searchPersonsReturn>
<attributes>
<attributes>
<name>ercreatedate</name>
<values>
<values>201104070130Z</values>
</values>
</attributes>
<attributes>
<name>status</name>
<values>
<values>Stuff1</values>
<values>Stuff2</values>
<values>Stuff3</values>
<values>Stuff4</values>
<values>Stuff5</values>
<values>Stuff6</values>
<values>Stuff7</values>
</values>
</attributes>
</attributes>
<itimDN>blah</itimDN>
<name>Smith, Bob</name>
<profileName>PER</profileName>
<select>false</select>
</searchPersonsReturn>
</searchPersonsResponse>
</soapenv:Body>
</soapenv:Envelope>
I'm trying to access the inner attribute node and pull out the Name and values into a multidimentional array like this ....
$array["status"][0]="stuff1";
$array["status"][1]="stuff2";
$array["status"][2]="stuff3";
$array["status"][3]="stuff4";
so far I have been able to access the nodes but not really get them the way I want. here is the code I have been playing around with .....
$dom_document = new DOMDocument();
$dom_document->loadXML($thexml);
$tag_els_names = $dom_document->getElementsByTagname('name');
$tag_els_values = $dom_document->getElementsByTagname('values');
$data = array();
$data2 = array();
foreach($tag_els_names as $node){
$data[] = array($node->nodeName => $node->nodeValue);
//grabs all the <name> node values
}
$i=0;$j=0;
foreach($tag_els_values as $node){
$j=0;
foreach($node->childNodes as $child) {
$data2[$i][$j] = $child->nodeValue;
//grabs all the value node values
$j++;
}
$i++;
$j=0;
}
Does anyone know an easy way to do this? I think that I have been looking at this for way to long.
How about something like:
$dom_document = new DOMDocument();
$dom_document->loadXML($thexml);
$xpath = new DOMXpath($dom_document);
$attr = $xpath->evaluate("//attribute/attributes");
$names = array();
$values = array();
$i = 0;
foreach($attr as $attr_node) {
$values[i] = array();
foreach($xpath->evaluate("name", $attr_node) as $name){
$names[] = $name->nodeValue;
}
$foreach($xpath->evaluate("value", $attr_node) as $value){
$values[i][] = $value->nodeValue;
}
i++;
}
This would, however, miss the <name> element that's outside of the <attributes> group. Did you mean to be including that?
I figured this out and thought it could help someone else
$doc = new DOMDocument();
$values=array();
if ($doc->loadXML($temp)) {
$attributes = $doc->getElementsByTagName('attributes');
foreach($attributes as $attribute) {
if($attribute->childNodes->length) {
$previous_nodeValue="";
foreach($attribute->childNodes as $i) {
if($i->nodeValue=="status"){
$previous_nodeValue=$i->nodeValue;
}
if($i->nodeName=="values" && $previous_nodeValue== "status"){
foreach($i->childNodes as $j){
$values[]=$j->nodeValue;
}
}
}
$previous_nodeValue="";
}
}
}

PHP only replace 1 node of a series

I have an XML file that currently has 4 nodes with the same name:
The file looks like this: (data.xml)
<?xml version="1.0"?>
<products>
<product>
<ItemId>531670</ItemId>
<modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
<name>Buy</name>
<name>Car, Marine & GPS</name>
<name>Car Installation Parts</name>
<name>Deck Installation Parts</name>
<name>Antennas & Adapters</name>
</product>
</products>
There are 4 nodes with the same name. (the name node).
I then use this PHP code to replace the node names
<?php
/**
* #param $xml string Your XML
* #param $old string Name of the old tag
* #param $new string Name of the new tag
* #return string New XML
*/
function renameTags($xml, $old, $new)
{
$dom = new DOMDocument();
$dom->loadXML($xml);
$nodes = $dom->getElementsByTagName($old);
$toRemove = array();
foreach ($nodes as $node)
{
$newNode = $dom->createElement($new);
foreach ($node->attributes as $attribute)
{
$newNode->setAttribute($attribute->name, $attribute->value);
}
foreach ($node->childNodes as $child)
{
$newNode->appendChild($node->removeChild($child));
}
$node->parentNode->appendChild($newNode);
$toRemove[] = $node;
}
foreach ($toRemove as $node)
{
$node->parentNode->removeChild($node);
}
return $dom->saveXML();
}
// Load XML from file data.xml
$xml = file_get_contents('data.xml');
$xml = renameTags($xml, 'name', 'newName');
echo $xml;
?>
This function replaces all of the name nodes with newName; however, I want to only replace one instance of the name tag because I want to rename each of the name tags.
If I call another
$xml = renameTags($xml, 'name', 'newName2');
It wont work, it will only use the first instance of $xml.
Any Idea how I can change my code to allow me to replace each name node individually?
If you simply want to rename the first node you encounter, add a break; statement at the end of your first foreach loop like this first example. However, this is horribly inefficient and a better way to do it is demonstrated in the second (CORRECT) example at the bottom.
THE WRONG WAY
Everytime you replace XML like this an angel sheds a tear ...
$src = "
<products>
<product>
<ItemId>531670</ItemId>
<modelNumber>METRA ELECTRONICS/MOBILE AUDIO</modelNumber>
<name>Buy</name>
<name>Car, Marine & GPS</name>
<name>Car Installation Parts</name>
<name>Deck Installation Parts</name>
<name>Antennas & Adapters</name>
</product>
</products>
";
function renameTags($xml, $old, $new)
{
$dom = new DOMDocument();
$dom->loadXML($xml);
$nodes = $dom->getElementsByTagName($old);
$toRemove = array();
foreach ($nodes as $node)
{
$newNode = $dom->createElement($new);
foreach ($node->attributes as $attribute)
{
$newNode->setAttribute($attribute->name, $attribute->value);
}
foreach ($node->childNodes as $child)
{
$newNode->appendChild($node->removeChild($child));
}
$node->parentNode->appendChild($newNode);
$toRemove[] = $node;
break;
}
foreach ($toRemove as $node)
{
$node->parentNode->removeChild($node);
}
$dom->formatOutput = TRUE;
return $dom->saveXML();
}
$xml = renameTags($src, 'name', 'newName');
echo $xml;
THE CORRECT WAY
function renameTags($xml, $old, $new)
{
$dom = new DOMDocument();
$dom->loadXML($xml);
// find the first node with the specified tag name
$oldNode = $dom->getElementsByTagName($old)->item(0);
// clone the node (deep copy)
$doppelganger = $oldNode->cloneNode(TRUE);
// import our cloned node to this dom document
$doppelganger = $dom->importNode($doppelganger, true);
// Create new node with the value from the copied node
$newNode = $dom->createElement($new, $doppelganger->nodeValue);
// update all the attributes of the new node with those from the copy
foreach ($doppelganger->attributes as $attrName => $attrNode) {
$newNode->setAttribute($attrName, $attrNode);
}
// append the newNode copy to the dom
$oldNode->parentNode->appendChild($newNode);
// remove the old node
$oldNode->parentNode->removeChild($oldNode);
$dom->formatOutput = TRUE;
return $dom->saveXML();
}

Need to show child data on parent id

i'm struggling with Xpath, i have an xml list and i need to get the child data based on the parent id ...
My xml file :
<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
the code i use to get for example the project 1 data (does not work):
$xmlDoc = new DOMDocument();
$xmlDoc->load(data.xml);
$xpath = new DOMXPath($xmlDoc);
$projectId = '1';
$query = '//projecten/project[#id='.$projectId.']';
$details = $xpath->query($query);
foreach( $details as $detail )
{
echo $detail->titel;
echo $detail->siteurl;
echo $detail->screenshot;
echo $detail->omschrijving;
}
But this does not show anything, if someone can point me out ... thanks
In addition to the solution already given you can also use:
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$projectNode->getElementsByTagName('titel')->item(0)->nodeValue,
$projectNode->getElementsByTagName('siteurl')->item(0)->nodeValue,
$projectNode->getElementsByTagName('screenshot')->item(0)->nodeValue,
$projectNode->getElementsByTagName('omschrijving')->item(0)->nodeValue;
}
or fetch the DOMText node values directly with Xpath
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$xpath->evaluate('string(titel)', $projectNode),
$xpath->evaluate('string(siteurl)', $projectNode),
$xpath->evaluate('string(screenshot)', $projectNode),
$xpath->evaluate('string(omschrijving)', $projectNode);
}
or import the node to SimpleXml
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
$detail = simplexml_import_dom($projectNode);
echo
$detail->titel,
$detail->siteurl,
$detail->screenshot,
$detail->omschrijving;
}
or even concatenate all the values directly in the XPath:
$xpath = new DOMXPath($dom);
echo $xpath->evaluate(
sprintf(
'concat(
/projecten/project[#id = %1$d]/titel,
/projecten/project[#id = %1$d]/siteurl,
/projecten/project[#id = %1$d]/screenshot,
/projecten/project[#id = %1$d]/omschrijving
', $id
)
);
Accessing the child nodes as you do:
echo $detail->title;
Is not valid, if you use DOM* functions. This would probably work if you were using SimpleXML.
For DOM* try this:
$dom = new DOMDocument;
$dom->loadXml('<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
');
$id = 2;
$xpath = new DOMXPath($dom);
foreach ($xpath->query(sprintf('/projecten/project[#id="%s"]', $id)) as $projectNode) {
// repeat this for every needed node
$titleNode = $xpath->query('titel', $projectNode)->item(0);
if ($titleNode instanceof DOMElement) {
echo $titleNode->nodeValue;
}
// or us a loop for all child nodes
foreach ($projectNode->childNodes as $childNode) {
echo $childNode->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