XML PHP Delete specific Child if? - php

I have a xml document looking like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<library>
<invites>
<invite>
<username>0</username>
<userid>0</userid>
</invite>
<invite>
<username>Danielle</username>
<gameid>87808</gameid>
</invite>
<invite>
<username>Petra</username>
<userid>978</userid>
</invite>
</invites>
</library>
Now, I want to delete the <invite> with Danielle but I am not sure how? I am using this right now but this will only delete the first record?
$file = 'my.xml';
$fp = fopen($file, "rb") or die("cannot open file");
$str = fread($fp, filesize($file));
$xml = new DOMDocument("1.0", "ISO-8859-1");
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->loadXML($str) or die("Error");
$root = $xml->documentElement;
$fnode = $root->firstChild;
$ori = $fnode->childNodes->item(0);
$fnode->removeChild($ori);
$xml->save($file);
I want to be able to delete depending on either gameid or userid. How do I go about this?
Thanks in advance :-)

You could use XPath to select all nodes that have a certain userid or gameid: http://www.w3schools.com/xpath/default.asp

Try this:
Using DOM & DOMXPath.
<?php
$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>
<invites>
<invite>
<username>0</username>
<userid>0</userid>
</invite>
<invite>
<username>Danielle</username>
<gameid>87808</gameid>
</invite>
<invite>
<username>Petra</username>
<userid>978</userid>
</invite>
</invites>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
//find all 'invite' nodes with username=Danielle and delete'em.
$node = $xpath->query("//invite[username='Danielle']");
// if found, append the new "value" node
if( $node->length ) {
foreach ($node as $n) {
$n->parentNode->removeChild( $n );
}
}
header('content-type: text/xml');
echo $dom->saveXML();
?>
Hope this helps.

Related

XML PHP Format Setup

Is there a why to do this? I'm new on create DomDocument. Thank you
<!DOCTYPE Data SYSTEM "http://data.data.org/schemas/data/1.234.1/data.dtd"> <Data payloadID = "123123123131231232323" timestamp = "2015-06-10T12:59:09-07:00">
$aribaXML = new DOMImplementation;
$dtd = $aribaXML->createDocumentType('cXML', '', 'http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd');
$dom = $aribaXML->createDocument('', '', $dtd);
Here are two ways in DOM to create a new document in PHP. If you don't need the DTD you can directly create an instance of the DOMDocument class.
$document = new DOMDocument('1.0', "UTF-8");
$document->appendChild(
$cXML = $document->createElement('cXML')
);
echo $document->saveXML();
Output:
<?xml version="1.0" encoding="UTF-8"?>
<cXML/>
For a document with a DTD your approach was correct.
$implementation = new DOMImplementation;
$dtd = $implementation->createDocumentType(
'cXML', '', 'http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd'
);
$document = $implementation->createDocument("", "cXML", $dtd);
$document->encoding = 'UTF-8';
$cXML = $document->documentElement;
echo $document->saveXML();
Output:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd">
After the initial bootstrap you use methods of the $document to create nodes and append them.
// set an attribute on the document element
$cXML->setAttribute('version', '1.1.007');
// xml:lang is a namespaced attribute in a reserved namespace
$cXML->setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:lang', 'en-US');
// nested elements
// create a node, store it for the next call and append it
$cXML->appendChild(
$header = $document->createElement('Header')
);
$header->appendChild(
$from = $document->createElement('From')
);
$from->appendChild(
$credential = $document->createElement('Credential')
);
// "Identity" has only a text node so we don't need to store it for later
$credential->appendChild(
$document->createElement('Identity')
)->textContent = '83528721';
// format serialized XML string
$document->formatOutput = TRUE;
echo $document->saveXML();
Output:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/1.2.014/cXML.dtd">
<cXML version="1.1.007" xml:lang="en-US">
<Header>
<From>
<Credential>
<Identity>83528721</Identity>
</Credential>
</From>
</Header>
</cXML>
$xml = new DomDocument('1.0');
$xml->formatOutput = true;
$works = $xml->createElement("works");
$xml->appendChild($works);
$work = $xml->createElement("work");
$work->setAttribute("id",1);
$works->appendChild($work);
$xml->save("storage/document.xml") or die("Error, Unable to create XML File");

How to append multiple XML files

I have a large XML file which takes forever to load on external FTP editor so I would like to split it into several files.
I use simplexml_load_file but couldn't make it load multiple files into same array.
XML files has the same structure, here's a sample of each file:
a.xml:
<?xml version="1.0"?>
<queries>
<query>
<admin_user_type>user</admin_user_type>
<show_in_menu>True</show_in_menu>
<title>Dashboard</title>
<vertical_report>True</vertical_report>
</query>
</queries>
b.xml:
<?xml version="1.0"?>
<queries>
<query>
<admin_user_type>admin</admin_user_type>
<show_in_menu>false</show_in_menu>
<title>Profile Element</title>
<input_name>Id</input_name>
<input_type>hidden</input_type>
</query>
</queries>
There are about 200 queries in 7,000 lines between the two files.
The code fails on the foreach below:
$xml = read_xml_files();
foreach($xml->children() as $xml) {
...
...
}
Below is what I've taken from Nigel's suggestion:
function read_xml_files() {
$target = new DOMDocument();
$target->loadXML('<base />');
mergeFile($target, XML_FILES_FOLDER."a.xml");
mergeFile($target, XML_FILES_FOLDER."b.xml");
$xml = simplexml_import_dom($target);
return $xml->asXML();
}
function mergeFile ( DOMDocument $target, $fileName ) {
$source = new DOMDocument();
$source->load($fileName);
foreach ( $source->childNodes as $row ) {
$import = $target->importNode($row, true);
$target->documentElement->appendChild($import);
}
}
Thanks for any suggestion!
If you use DOMDocument to do the main merging part...
function mergeFile ( DOMDocument $target, $fileName ) {
$source = new DOMDocument();
$source->load($fileName);
foreach ( $source->childNodes as $row ) {
$import = $target->importNode($row, true);
$target->documentElement->appendChild($import);
}
}
$target = new DOMDocument();
$target->loadXML('<base />');
mergeFile($target, "file1.xml");
mergeFile($target, "file2.xml");
$xml = simplexml_import_dom($target);
echo $xml->asXML();
At the end $xml contains the merged files.
Note that this adds another level to the document (unless I know the type of contents it's difficult to do anything else). So in this case you will get something like...
file1.xml
<?xml version="1.0" encoding="UTF-8"?>
<A>A</A>
file2.xml
<?xml version="1.0" encoding="UTF-8"?>
<B>B</B>
Result will be
<?xml version="1.0"?>
<base>
<A>A</A>
<B>B</B>
</base>
Update:
You could also try...
function read_xml_files() {
$target = new DOMDocument();
$target->loadXML('<queries />');
mergeFile($target, "NewFile.xml");
mergeFile($target, "NewFile1.xml");
$xml = simplexml_import_dom($target);
return $xml->asXML();
}
function mergeFile ( DOMDocument $target, $fileName ) {
$source = new DOMDocument();
$source->load($fileName);
foreach ( $source->getElementsByTagName("query") as $row ) {
$import = $target->importNode($row, true);
$target->documentElement->appendChild($import);
}
}
This merges the file so that the result is
<?xml version="1.0"?>
<queries>
<query>
<admin_user_type>user</admin_user_type>
<show_in_menu>True</show_in_menu>
<title>Dashboard</title>
<vertical_report>True</vertical_report>
</query>
<query>
<admin_user_type>admin</admin_user_type>
<show_in_menu>false</show_in_menu>
<title>Profile Element</title>
<input_name>Id</input_name>
<input_type>hidden</input_type>
</query>
</queries>

How to delete and edit specific xml node using PHP?

I am practicing on XML and PHP. I can not find out how to delete the whole xml node. Here is my XML:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<playlist_name>Channel List</playlist_name>
<category>
<category_id>1</category_id>
<category_title>HD</category_title>
</category>
<channel>
<title>VTC HD1</title>
<stream_url><![CDATA[http://203.162.16.22:80/lives/origin01/vtchd1.isml/vtchd1.m3u8]]></stream_url>
<logo_30x30>vtchd1.jpg</logo_30x30>
<category_id>1</category_id>
</channel>
<channel>
<title>VTC HD2</title>
<stream_url><![CDATA[http://203.162.16.22:80/lives/origin01/vtchd2.isml/vtchd2.m3u8]]></stream_url>
<logo_30x30>vtchd2.jpg</logo_30x30>
<category_id>1</category_id>
</channel>
</items>
And here is the code of two function which I am having problem, delete() and edit()
<?php
function delete_channel()
{
$file = "nStream.xml";
$fp = fopen($file, "rb") or die("cannot open file");
$str = fread($fp, filesize($file));
$xml = new DOMDocument();
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->loadXML($str) or die("Error");
// original
// echo "<xmp>OLD:\n". $xml->saveXML() ."</xmp>";
// get document element
$root = $xml->documentElement;
$fnode = $root->firstChild;
//get a node
$ori = $fnode->childNodes->item(0);
// remove
$fnode->removeChild($ori);
// echo "<xmp>NEW:\n". $xml->saveXML() ."</xmp>";
}
function edit_channel()
{
echo 'Go Edit';
}
For the delete_channel(), nothing is deleted when I run that function. I want each time I use that function, one in xml file ll be deleted.
delete a <channel> with simplexml:
$xml = simplexml_load_file($filename);
// select the channel to be deleted by title
$channel = $xml->xpath("//channel[title='VTC HD2']");
// delete it!
unset($channel[0][0]);
// save it
$xml->asXML($filename);
see it working: https://eval.in/37084

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