2010-07-29 8 views
7

dire que j'ai XML:PHP SimpleXML: insérez le noeud à certaine position

<root> 
    <nodeA /> 
    <nodeA /> 
    <nodeA /> 
    <nodeC /> 
    <nodeC /> 
    <nodeC /> 
</root> 

Comment insérer "nodeB" entre As et Cs? En PHP, de préférence via SimpleXML? Comme:

<root> 
    <nodeA /> 
    <nodeA /> 
    <nodeA /> 
    <nodeB /> 
    <nodeC /> 
    <nodeC /> 
    <nodeC /> 
</root> 
+1

Je ne suis pas sûr que vous le pouvez. De la documentation, il semble que SimpleXML soit destiné à une manipulation ** vraiment ** simple. – MvanGeest

+0

Est-ce que je ne peux pas stocker les Cs ailleurs, les supprimer, ajouter B et ajouter Cs à partir de cet emplacement temporaire? Je ne suis pas très versé dans PHP ... –

Répondre

13

Voici une fonction permettant d'insérer un nouveau SimpleXMLElement après un autre SimpleXMLElement. Comme ce n'est pas directement possible avec SimpleXML, il utilise des classes/méthodes DOM en arrière-plan pour faire le travail.

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); 
    } 
} 

Et un exemple de la façon dont il pourrait être utilisé (spécifique à votre 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(); 

Si vous voulez/besoin d'une explication de comment cela fonctionne (le code est assez simple mais pourrait inclure des concepts étrangers), il suffit de demander.

+0

Est-ce moi, ou est-ce que le paramètre $ sxe dans simplexml_insert_after n'est jamais utilisé? – GolezTrol

+1

Ce n'est pas toi, pourquoi ne pas t'amuser en répondant aux questions? :) – salathe

+0

Oui, pourquoi pas. :) Merci pour votre réponse, b.t.w. J'ai pris la librerie pour l'utiliser pour construire la mienne. :) – GolezTrol

4

La réponse de Salathe m'a aidé, mais comme j'ai utilisé la méthode addChild de SimpleXMLElement, j'ai cherché une solution pour rendre l'insertion des enfants comme un premier enfant plus transparente. La solution est de prendre cette fonctionnalité en fonction de DOM et de le cacher dans une sous-classe de SimpleXMLElement:

class SimpleXMLElementEx extends SimpleXMLElement 
{ 
    public function insertChildFirst($name, $value, $namespace) 
    { 
     // Convert ourselves to DOM. 
     $targetDom = dom_import_simplexml($this); 
     // Check for children 
     $hasChildren = $targetDom->hasChildNodes(); 

     // Create the new childnode. 
     $newNode = $this->addChild($name, $value, $namespace); 

     // Put in the first position. 
     if ($hasChildren) 
     { 
      $newNodeDom = $targetDom->ownerDocument->importNode(dom_import_simplexml($newNode), true); 
      $targetDom->insertBefore($newNodeDom, $targetDom->firstChild); 
     } 

     // Return the new node. 
     return $newNode; 
    } 
} 

Après tout, SimpleXML permet de spécifier quelle classe l'élément à utiliser:

$xml = simplexml_load_file($inputFile, 'SimpleXMLElementEx'); 

Maintenant, vous pouvez appeler insertChildFirst sur n'importe quel élément pour insérer le nouvel enfant en tant que premier enfant. La méthode renvoie le nouvel élément en tant qu'élément SimpleXML, son utilisation est donc similaire à addChild. Bien sûr, il serait facilement possible de créer une méthode insertChild qui permet de spécifier un élément exact pour insérer l'élément avant, mais puisque je n'en ai pas besoin maintenant, j'ai décidé de ne pas le faire.