2010-06-16 10 views
2

Je souhaite insérer un noeud avec des enfants à un emplacement spécifique dans le fichier XML. Comment fait-on ça?PHP XML - Insertion d'un noeud XML à un emplacement spécifique

Par exemple. Si j'ai un XML comme:

<myvalues> 
<image name="img01"> 
    <src>test</src> 
</image> 

<image name="img02"> 
    <src>test</src> 
</image> 

<image name="img03"> 
    <src>test</src> 
</image> 
</myvalues> 

Je veux insérer:

<image name="img11"> 
    <src>test2</src> 
</image> 

entre <image name="img01"> & <image name="img02">. Comment puis-je faire cela? J'utilise SimpleXML dès maintenant pour lire le XML.

Merci.

EDIT: J'ai essayé le code suivant. Mais, le nouveau noeud est ajouté au bas du XML en dehors de la structure XML.

$xml = new DomDocument(); 


$xml->preserveWhitespace = false; 
$xml->load('myXMLFile.xml'); 

$newNode = $xml->createElement('tryimage'); 

$xpath = new DOMXpath($xml); 
$elements = $xpath->query('/myvalues/image[name="img01"]'); 

$refNode = $elements->item(0); 

$xml->insertBefore($newNode, $refNode->nextSibling); 


header('Content-Type: text/plain'); 
echo $xml->saveXML(); 

La sortie est quelque chose comme ceci:

<xml....> 
    <myvalues> 
    <image name="01"> 
    </image> 
    . 
    . 
    . 
    </myvalues> 
<tryimage /> 

Répondre

2

Eh bien, il n'y a aucun moyen facile que je peux voir avec SimpleXML (il est censé être simple après tout). Une façon, serait de déplacer le nœud myvalues ​​sur DOMDocument, d'y ajouter le nœud, puis de le remplacer par le nœud dom. Étant donné que $myvalues est votre noeud <myvalues> 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); 

Remarque, il n'y a pas besoin de se reconvertir en SimpleXML, étant donné que toute modification de la DOMElement seront automatiquement appliqués à la version SimpleXML ... Il ajoutera automatiquement le nouvel enfant si elle ne trouve pas le $refNode (parce qu'il n'existe pas, etc) ...

EDIT: Ajout XPath

Remplacer le bloc foreach avec ce (équivalent Fonctionnellement, si je suis la requête à droite):

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

Depuis DOMNodeList::item() renvoie la valeur NULL un décalage inexistant, nous ne même pas besoin de vérifier pour voir s'il y a des éléments qu'il contient.

Maintenant, vous devrez peut-être/voulez ajuster la requête xpath pour être plus/moins spécifique. Voici un decent tutorial ...

Edit 2

J'ai oublié que XPath avait besoin d'un personnage @ pour lui dire de vérifier un attribut.

Voici mon code de travail (puisque je ne connais pas votre schéma exact):

$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"]'); 
+0

j'utiliser un XPath pour arriver au noeud d'image par son nom directement, mais à part ça +1 – Gordon

+0

@Gordon: comment l'utilisation du XPath? @ircmaxell: Merci. Ce qui dans le cas je ne sais pas avant quel noeud je dois ajouter mais je sais après quel noeud je dois ajouter. Dans ce cas, puis-je utiliser insertBefore? Probablement quelque chose comme insertBefore ($ newNode, $ refNode-> nextSibling)? Est-ce que cela a du sens? – Blueboye

+1

Bien sûr. DomNode-> nextSibling renverra null si c'est le dernier dans la liste, donc l'opération sera toujours cohérente et déterministe. Je vais éditer ma réponse avec le bit XPath ... – ircmaxell

2

Vous pouvez, comme mentionné dans ircmaxel's answer faire usage des classes DOM/méthodes pour faire ce que vous voulez. Ci-dessous est un exemple concis (probablement moins de code que vous ne voudrez vraiment utiliser ) d'insérer un SimpleXMLElement après un autre.

function insertAfter(SimpleXMLElement $new, SimpleXMLElement $target) { 
    $target = dom_import_simplexml($target); 
    $new = $target->ownerDocument->importNode(dom_import_simplexml($new), true); 
    if ($target->nextSibling) { 
     $target->parentNode->insertBefore($new, $target->nextSibling); 
    } else { 
     $target->parentNode->appendChild($new); 
    } 
} 

$sxe = new SimpleXMLElement($xml); 
$img = $sxe->xpath('//image[@name="img01"]'); // xpath returns an array 
if (!empty($img)) { 
    $new = new SimpleXMLElement('<image name="img11" foo="bar"><src>test</src></image>'); 
    insertAfter($new, $img[0]); 
} 

foreach ($sxe as $image) { 
    echo $image['name'] . PHP_EOL; 
} 
/* 
    img01 
    img11 
    img02 
    img03 
*/ 
Questions connexes