PHP XML Remove Parent - php

XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<pages>
<page><title>Home</title><content>Lorem Ipsum</content></page>
<page><title>Pictures</title><content>Lorem Ipsum</content></page>
<page><title>Information</title><content>Lorem Ipsum</content></page>
</pages>
<css>
<css-tag><title>background-color</title><value>#FFF</value></css-tag>
</css>
<layout>1</layout>
</root>
PHP:
$title = $_GET['0'];
$xml = new DOMDocument('1.0', 'ISO-8859-1');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = true;
$xml->load($location);
$pages = $xml->getElementsByTagName("page");
foreach($pages as $page){
$pagetitle = $page->getElementsByTagName("title");
$pagetitlevalue = $pagetitle->item(0)->nodeValue;
if($title == $pagetitlevalue){
$pagetitle->item(0)->parentNode->removeChild($pagetitle->item(0));
}
}
$xml->save($location);
This code gets rid of just the <Title> node, how can it be changed to get rid of the parent <Page> node?
I can't figure out how to do this, I just manage to get rid of the title node and get loads of error codes

Whilst this is something I'd probably use xpath for, you can find...
$pagetitle->item(0)->parentNode->removeChild($pagetitle->item(0));
and replace with...
$pagetitle->item(0)->parentNode->parentNode->removeChild($pagetitle->item(0)->parentNode);
to go one level higher in your XML tree

Related

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;

both appendChild and insertBefore when called on DOMDocument append node after root

I have a function to add a valueless element and a bunch of child elements with values to an xml file, so I wrote it to take two arguments $tag(String the parent node) and $hash(Array $elm=>$elm_value)
function addUser($tag, $hash)
{
$dom = new DOMDocuemnt();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('file');
$parent = $dom->createElement($tag);
foreach($hash as $elm => $value){
$n = $dom->createElement($elm, $value);
$parent->appendChild($n);
}
$dom->appendChild($parent);
$dom->save('file');
return $dom->saveXML();
}
Only problem is $dom->appendChild($parent) appends everything after the root element's closing tag, mucking up my xml file. So I tried $dom->insertBefore($parent) with the same result. So instead I tried $xpath = new DOMXPath($dom); $root = $xpath->query('/')->item(0); $root->appendChild($parent);. Same result. Then I tried selecting the root element with $dom->getElementsByTagName(name of root)->item(0); And was surprised when that actually worked! But what if I don't know the tag name? Is there another way to select the root element so that calling appendChild or inserBefore will add the element before the root closing tag instead of after it?
This seems to work:
Initial XML file -
<!-- test.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node Id="1">
<Clicks>click1</Clicks>
</node>
</root>
PHP -
<?php
function addUser($tag, $hash) {
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('test.xml');
$parent = $dom->createElement($tag);
$dom->documentElement->appendChild($parent);
foreach($hash as $elm => $value){
$n = $dom->createElement($elm);
$n->appendChild( $dom->createTextNode( $value ) );
$parent->appendChild($n);
}
$dom->save('test.xml');
}
$arr = array( 'name' => 'pushpesh', 'age' => 30, 'profession' => 'SO bugger' );
addUser('user', $arr);
XML file is now -
<!-- test.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node Id="1">
<Clicks>click1</Clicks>
</node>
<user>
<name>pushpesh</name>
<age>30</age>
<profession>SO bugger</profession>
</user>
</root>
Hope this helps.

Appending Nodes from a DOM Object to another DOM

Here is what i have as best i can figure what needs to be done. But im just at a loss as to how to get it to work.
Both $xml and $xmlfile are DOM objects/XML. $xml is the source of the data i want to append into $mxlfile.
$xml is sent in to the function from another function as a dom object.
This is how the xml comes to me.
<?xml version="1.0" encoding="utf-8"?>
<!-- Automatically generated data from EVE-Central.com -->
<!-- This is the new API :-) -->
<evec_api version="2.0" method="marketstat_xml">
<marketstat>
<type id="18">
<all>
<volume>1934078988.00</volume>
<avg>98.31</avg>
<max>26721.00</max>
<min>0.53</min>
<stddev>1339.78</stddev>
<median>26.31</median>
<percentile>0.00</percentile>
</all>
<buy>
<volume>1894081100.00</volume>
<avg>20.77</avg>
<max>31.31</max>
<min>0.53</min>
<stddev>7.25</stddev>
<median>26.00</median>
<percentile>28.17</percentile>
</buy>
<sell>
<volume>39997888.00</volume>
<avg>34.32</avg>
<max>26721.00</max>
<min>4.01</min>
<stddev>2339.28</stddev>
<median>29.80</median>
<percentile>26.76</percentile>
</sell>
</type>
<type id="ectera">
</type>
</marketstat>
</evec_api>
And here the part of my function i just cant get working.
$xml->preserveWhiteSpace = FALSE;
$xml->formatOutput = TRUE;
if(is_file($file)){
$xmlfile = new DOMDocument;
$xmlfile->load($file);
$dst = $xmlfile->getElementsByTagName('marketStat');
foreach($xml->getElementsByTagName('type') as $child){
$dst->appendChild($xmlfile->importNode($child));}
$xmlfile->save($file);
}
I know there's something wrong, but im just teaching myself xml and dom, so help would be much appreciated.
<?php
if(is_file($file))
{
$xmlfile = new DOMDocument;
$xml->preserveWhiteSpace = FALSE;
$xml->formatOutput = TRUE;
$xmlfile->load($file);
$dst = $xmlfile->getElementsByTagName('marketStat');
$parentLength = $dst->length;
for($i=0; $i< $parentLength ; $i++)
{
foreach($xmlfile->getElementsByTagName('type') as $child)
{
$dst->item($i)->appendChild($child);
}
}
$xmlfile->save($file);
}
Your code has two mistakes
You can set options for DOM parser after you create the document.
You can not use appendChild on list of DOM object. You have to add them on indivisual DOM element one by one.

PHP - SimpleXML - AddChild with another SimpleXMLElement

I'm trying to build a rather complex XML document.
I have a bunch of sections of the XML document that repeats. I thought I'd use multiple string templates as base document for the sections and create instances of XML elements using simplexml_load_string.
So I have one instance of SimpleXMLElement as the base document
$root =
simplexml_load_string($template_root);
then I loop through some items in my database, create new SimpleXMLElement, something like this:
for (bla bla bla):
$item = simplexml_load_string($template_item);
// do stuff with item
// try to add item to the root document..
// Stuck here.. can't do $root->items->addChild($item)
endfor;
I can't call addChild because it just expects a tag name and value.. you can't addChild another SimpleXMLElement.
Am I missing something here? seems really dumb that addChild can't take a SimpleXMLELement as a parameter.
Is there any other way to do this? (apart from using a different xml lib)
As far as I know, you can't do it with SimpleXML because addChild doesn't make a deep copy of the element (being necessary to specify the tag name can easily be overcome by calling SimpleXMLElement::getName()).
One solution would be to use DOM instead:
With this function:
function sxml_append(SimpleXMLElement $to, SimpleXMLElement $from) {
$toDom = dom_import_simplexml($to);
$fromDom = dom_import_simplexml($from);
$toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}
We have for
<?php
header("Content-type: text/plain");
$sxml = simplexml_load_string("<root></root>");
$n1 = simplexml_load_string("<child>one</child>");
$n2 = simplexml_load_string("<child><k>two</k></child>");
sxml_append($sxml, $n1);
sxml_append($sxml, $n2);
echo $sxml->asXML();
the output
<?xml version="1.0"?>
<root><child>one</child><child><k>two</k></child></root>
See also some user comments that use recursive functions and addChild, e.g. this one.
You could use this function that is based in creating the children with attributes from the source:
function xml_adopt($root, $new) {
$node = $root->addChild($new->getName(), (string) $new);
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
foreach($new->children() as $ch) {
xml_adopt($node, $ch);
}
}
$xml = new SimpleXMLElement("<root/>");
$child = new SimpleXMLElement("<content><p a=\"aaaaaaa\">a paragraph</p><p>another <br/>p</p></content>");
xml_adopt($xml, $child);
echo $xml->asXML()."\n";
This will produce:
<?xml version="1.0"?>
<root><content><p a="aaaaaaa">a paragraph</p><p>another p<br/></p></content></root>
The xml_adopt() example doesn't preserve namespace nodes.
My edit was rejected because it changed to much? was spam?.
Here is a version of xml_adopt() that preserves namespaces.
function xml_adopt($root, $new, $namespace = null) {
// first add the new node
// NOTE: addChild does NOT escape "&" ampersands in (string)$new !!!
// replace them or use htmlspecialchars(). see addchild docs comments.
$node = $root->addChild($new->getName(), (string) $new, $namespace);
// add any attributes for the new node
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
// get all namespaces, include a blank one
$namespaces = array_merge(array(null), $new->getNameSpaces(true));
// add any child nodes, including optional namespace
foreach($namespaces as $space) {
foreach ($new->children($space) as $child) {
xml_adopt($node, $child, $space);
}
}
}
(edit: example added)
$xml = new SimpleXMLElement(
'<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<channel></channel></rss>');
$item = new SimpleXMLElement(
'<item xmlns:media="http://search.yahoo.com/mrss/">
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>');
$channel = $xml->channel;
xml_adopt($channel, $item);
// output:
// Note that the namespace is (correctly) only preserved on the root element
'<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<item>
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>
</channel>
</rss>'

Adding node using PHP's SimpleXML to XML with namespaces

<?xml version="1.0" encoding="ISO-8859-2"?>
<!DOCTYPE pasaz:Envelope SYSTEM "loadOffers.dtd">
<pasaz:Envelope xmlns:pasaz="http://schemas.xmlsoap.org/soap/envelope/">
<pasaz:Body>
<loadOffers xmlns="urn:ExportB2B">
<offers />
</loadOffers>
</pasaz:Body>
</pasaz:Envelope>
I've to add some child nodes to "offers" node and I'm using SimpleXML.
The PHP code: $offer = $xml->offers->addChild('offer') returns an error.
It's all wrong because I've got problem with handling namespaces in SimpleXML! Please help!
E.g. by using xpath the get the target/parent element.
<?php
$envelope = new SimpleXMLElement('<?xml version="1.0" encoding="ISO-8859-2"?>
<!DOCTYPE pasaz:Envelope SYSTEM "loadOffers.dtd">
<pasaz:Envelope xmlns:pasaz="http://schemas.xmlsoap.org/soap/envelope/">
<pasaz:Body>
<loadOffers xmlns="urn:ExportB2B">
<offers />
</loadOffers>
</pasaz:Body>
</pasaz:Envelope>');
$envelope->registerXPathNamespace('pasaz', 'http://schemas.xmlsoap.org/soap/envelope/');
$envelope->registerXPathNamespace('b2b', 'urn:ExportB2B');
$ns = $envelope->xpath('//pasaz:Body/b2b:loadOffers/b2b:offers');
if ( 0<count($ns) ) {
$offers = $ns[0];
$offers->a = 'abc';
$offers->x = 'xyz';
}
echo $envelope->asXml();
prints
<?xml version="1.0" encoding="ISO-8859-2"?>
<!DOCTYPE pasaz:Envelope SYSTEM "loadOffers.dtd">
<pasaz:Envelope xmlns:pasaz="http://schemas.xmlsoap.org/soap/envelope/">
<pasaz:Body>
<loadOffers xmlns="urn:ExportB2B">
<offers><a>abc</a><x>xyz</x></offers>
</loadOffers>
</pasaz:Body>
</pasaz:Envelope>

Categories