PHP Simplexml: Insert Node at Certain Position

PHP SimpleXML: insert node at certain position

The following is a function to insert a new SimpleXMLElement after some other SimpleXMLElement. Since this isn't directly possible with SimpleXML, it uses some DOM classes/methods behind-the-scenes to get the job done.

function simplexml_insert_after(SimpleXMLElement $insert, SimpleXMLElement $target)
{
$target_dom = dom_import_simplexml($target);
$insert_dom = $target_dom->ownerDocument->importNode(dom_import_simplexml($insert), true);
if ($target_dom->nextSibling) {
return $target_dom->parentNode->insertBefore($insert_dom, $target_dom->nextSibling);
} else {
return $target_dom->parentNode->appendChild($insert_dom);
}
}

And an example of how it might be used (specific to your question):

$sxe = new SimpleXMLElement('<root><nodeA/><nodeA/><nodeA/><nodeC/><nodeC/><nodeC/></root>');
// New element to be inserted
$insert = new SimpleXMLElement("<nodeB/>");
// Get the last nodeA element
$target = current($sxe->xpath('//nodeA[last()]'));
// Insert the new element after the last nodeA
simplexml_insert_after($insert, $target);
// Peek at the new XML
echo $sxe->asXML();

If you want/need an explanation of how this works (the code is fairly simple but might include foreign concepts), just ask.

PHP XML - Inserting a XML node at a specific location

Well, there's no easy way that I can see with SimpleXML (it's supposed to be simple after all).

One way, would be to move the myvalues node over to DOMDocument, add the node there, then replace it with the dom node. Given that $myvalues is your <myvalues> node in SimpleXML:

$domMyValues = dom_import_simplexml($myvalues);
$newNode = $domMyValues->ownerDocument->createElement('mynewelement');
//Apply attributes and whatever to $newNode

//find the node that you want to insert it before (from the $domMyValues class
$nodes = $domMyValues->getElementsByTagName('image');
$refNode = null;
foreach ($nodes as $node) {
if ($node->getAttribute('name') == 'img02') {
$refNode = $node;
}
}
$domMyValues->insertBefore($newNode, $refNode);

Note, there's no need to convert back to SimpleXML, since any changes to the DOMElement will be applied automatically to the SimpleXML version... It will automatically append the new child if it can't find the $refNode (because it doesn't exist, etc)...

EDIT: Adding XPath

Replace the foreach block with this (Functionally equivalent, if I got the query right):

$xpath = new DOMXpath($domMyValues->ownerDocument);
$elements = $xpath->query('//image[@name="img02"]');
$refNode = $elements->item(0);

Since DOMNodeList::item() returns null for a non-existent offset, we don't even need to check to see if there are items in it.

Now, you may need/want to adjust the xpath query to be more/less specific. Here's a decent tutorial...

Edit 2

I forgot that xpath needed an @ character to tell it to check an attribute.

Here's my working code (since I don't know your exact schema):

$x = '<?xml version="1.0" ?>
<myvalues>
<images>
<image name="01">Foo</image>
<image name="02">Bar</image>
</images>
</myvalues>';

$dom = new DomDocument();
$dom->loadXML($x);
$xpath = new DOMXpath($dom);

$elements = $xpath->query('//images/image[@name="01"]');
$elements = $xpath->query('//image[@name="01"]');
$elements = $xpath->query('/myvalues/images/image[@name="01"]');

Add new child node with php simplexml on top of file

Be aware that SimpleXML is a very simple implementation of a XML API in PHP.

So, there's no SimpleXML function that directly achieves what you want. But: You can use a different approach of building your XML structure:

<?php

$newXml = simplexml_load_string('<?xml version="1.0" encoding="utf-8"?><News></News>');

$entry = $newXml->addChild('NewsModel');
$entry->addChild('ID', $_POST['inputIDNumber']);
$entry->addChild('Headline', $_POST['inputHeadline']);
$entry->addChild('ShortDescription', $_POST['inputShorDesc']);
$entry->addChild('Description', $_POST['inputDesc']);
$entry->addChild('LinkText', $_POST['inputLinkText']);
$entry->addChild('Link', $_POST['inputLink']);

$xml = simplexml_load_file($xmlurl) or die("Kann keine Verbindung zu $xmlurl aufbauen");

foreach ($xml as $child) {
$entry = $newXml->addChild('NewsModel');
$entry->addChild('ID', $child->ID);
$entry->addChild('Headline', $child->Headline);
$entry->addChild('ShortDescription', $child->ShortDescription);
$entry->addChild('Description', $child->Description);
$entry->addChild('LinkText', $child->LinkText);
$entry->addChild('Link', $child->Link);
}
file_put_contents($xmlurl, $newXml->asXML(), 0, stream_context_create(['ftp' => ['overwrite' => true]]));

You start by creating a new XML structure and adding the new NewsModel entry to it. After that you import the existing NewsModels.

SimpleXML add element before

You can do something like this:

    $domelement = dom_import_simplexml($items);

$new = $dom->insertBefore(
$dom->ownerDocument->createElement("total"),
$dom->firstChild
);

$newsxml = simplexml_import_dom($new);

then add the items into total node.

Insert XML structure as a child of another XML Element using PHP

You can't with SimpleXML, but if you really need manipulate your DOM or create it from scratch consider DOMDocument.

$xmlObject   = new \DOMDocument();
$xml = $xmlObject->createElement('root');
$xml_a = $xmlObject->createElement('parent');
$xml_b = $xmlObject->createElement('child');
$xml_b->setAttribute('attribute','value');
$xml_b->appendChild(new \DOMElement('item', 'itemValue'));
$xml_a->appendChild($xml_b);
$xml->appendChild($xml_a);
$xmlObject->appendChild($xml);
echo $xmlObject->saveXML();

Using addChild with an index position

I know that Artefacto's answer has been accepted already but it looks to be doing more work than is required. A simpler alternative, using his function signature, could look like the function below.

function addchild_at(SimpleXMLElement $sxml, $tagname, $i)
{
$parent = dom_import_simplexml($sxml);
$child = $parent->ownerDocument->createElement($tagname);
$target = $parent->getElementsByTagname('*')->item($i);
if ($target === NULL) {
$parent->appendChild($child);
} else {
$parent->insertBefore($child, $target);
}
}

adding a new node in XML file via PHP

I'd use SimpleXML for this. It would look somehow like this:

// Open and parse the XML file
$xml = simplexml_load_file("questions.xml");
// Create a child in the first topic node
$child = $xml->topic[0]->addChild("subtopic");
// Add the text attribute
$child->addAttribute("text", "geography");

You can either display the new XML code with echo or store it in a file.

// Display the new XML code
echo $xml->asXML();
// Store new XML code in questions.xml
$xml->asXML("questions.xml");


Related Topics



Leave a reply



Submit