2010-07-29 16 views
7

decir que tengo XML:PHP SimpleXML: insertar nodo en cierta posición

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

¿Cómo inserto "enodoB" entre As y Cs? En PHP, preferiblemente a través de SimpleXML? Al igual que:

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

No estoy seguro de que pueda hacerlo. De la documentación, parece que SimpleXML está diseñado para una manipulación ** realmente ** simple. – MvanGeest

+0

¿No puedo de alguna manera almacenar C en otro lugar, eliminarlos, agregar B y volver a agregar Cs desde esa ubicación temporal? Simplemente no soy muy versado en PHP ... –

Respuesta

13

La siguiente es una función para insertar un nuevo SimpleXMLElement después de algún otro SimpleXMLElement. Como esto no es directamente posible con SimpleXML, utiliza algunas clases/métodos DOM detrás de escena para realizar el trabajo.

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

Y un ejemplo de cómo podría ser utilizado (específica a su pregunta):

$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 quiere/necesita una explicación de cómo funciona esto (el código es bastante simple, pero podría incluir conceptos extraños), solo pregunte.

+0

¿Soy yo o el parámetro $ sxe en simplexml_insert_after nunca utilizado? – GolezTrol

+1

No eres tú, ¿por qué no te diviertes al responder preguntas? :) – salathe

+0

Sí, ¿por qué no? :) Gracias por su respuesta, b.t.w. Tomé la libertad de usarlo para construir el mío. :) – GolezTrol

4

La respuesta de Salathe me ayudó, pero desde que usé el método addChild de SimpleXMLElement, busqué una solución para hacer que la inserción de niños como primer hijo sea más transparente. La solución es tomar esa funcionalidad basada en DOM y esconderlo en una subclase 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; 
    } 
} 

Después de todo, SimpleXML permite especificar qué clase de elemento de empleo:

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

Ahora puede llamar insertChildFirst en cualquier elemento para insertar al nuevo hijo como primer hijo. El método devuelve el nuevo elemento como un elemento SimpleXML, por lo que su uso es similar a addChild. Por supuesto, sería fácilmente posible crear un método insertChild que permita especificar un elemento exacto para insertar el elemento antes, pero como no lo necesito ahora, decidí no hacerlo.

Cuestiones relacionadas