4 votes

Obtenir un nœud ancêtre avec PHP et xpath

Veuillez me pardonner si ce qui suit est un peu confus, je me suis tué à la tâche en essayant de résoudre ce problème.

Il s'agit d'un morceau de XML (exporté d'un site beaucoup plus grand) que j'utilise pour créer un arbre de catégories pour un mini-CMS. Une fois que j'ai obtenu la valeur et le nom du nœud, ce qui n'est pas un problème, j'ai également besoin d'obtenir le "parent" de chaque nœud, c'est-à-dire le nœud qui le précède et qui se trouve au-dessus de lui dans la hiérarchie.

    <productCategory>
    <genericName>DigitalCinema</genericName>
    <productCategories>
      <productCategory>
        <genericName>DCinemaProj</genericName>
        <productModels>
          <productModel>ProjProd-1</productModel>
          <productModel>ProjProd-2</productModel>
          <productModel>ProjProd-3</productModel>
          <productModel>ProjProd-4</productModel>
        </productModels>
      </productCategory>
      <productCategory>
        <genericName>DCinemaLens</genericName>
      </productCategory>
    </productCategories>
  </productCategory>

Par exemple, pour l'élément productCategory-genericName DCinemaLens j'ai besoin de pouvoir saisir le parent en tant que DigitalCinema et de même pour l'individu produitModèle où le parent serait DCinemaProj .

J'ai essayé plusieurs requêtes différentes dans xpath en utilisant ancestor, previous-sibling et parent et je n'arrive toujours pas à récupérer le nœud dont j'ai besoin.

Voici mon code tel qu'il se présente après avoir abandonné mes tentatives il y a quelques minutes.

if ($xml->xpath('//productCategories')) {

    foreach($xml->xpath('//genericName | //productModel') as $genericName){

    echo "<p align='center'>$genericName";

    $type = $genericName->getName();

    echo " - (" . $type . ") ";

    $derp = $xml->xpath("ancestor::productCategory[1]/genericName");

    echo $derp;

    echo '</p>';

    }

    }

J'ai également réussi à obtenir des informations dans un tableau, mais cela renvoie toujours toutes les valeurs du XML.

$key = 'genericName';

    $derpgleep = $derp[$key];

    echo 'Derp= ' . $derpgleep;

    print_r($derp);

J'espère qu'il existe une solution très simple que je néglige. J'espère avoir été clair.

2voto

empo Points 12803

L'expression XPath que vous utilisez :

 ancestor::productCategory[1]/genericName

fonctionnerait si vous étiez en mesure d'exécuter cette expression à partir du nœud actuel. ce n'est pas possible ; en effet, à partir du nœud actuel, l'expression peut être exécutée. Array $genericName car il ne contient pas de parents, ni d'ancêtres.

Je pense que votre option est de retraverser toute l'arborescence XML. Voici un exemple de test qui fonctionne comme prévu, sur la base de votre échantillon d'entrée.

<?php
$xml = simplexml_load_file("test_input1.xml");

if ($xml->xpath('//productCategories')) {

    foreach($xml->xpath('//genericName') as $genericName){

        echo "<p align='center'>$genericName";

        $type = $genericName->getName();

        echo " - (" . $type . ") ";

        $derp = $xml->xpath("//genericName[.='" . 
            $genericName[0] . 
            "']/ancestor::productCategory[2]/genericName");

        echo $derp[0];  echo "</p>\n";
    }
}
?>

Le fragment HTML suivant sera imprimé :

<p align='center'>DigitalCinema - (genericName) </p>
<p align='center'>DCinemaProj - (genericName) DigitalCinema</p>
<p align='center'>DCinemaLens - (genericName) DigitalCinema</p>

Alors que pour obtenir le "parent" de produitModèle vous avez besoin d'un xpath comme :

        $derp = $xml->xpath("//productModel[.='" . 
            $productModel[0] . 
            "']/parent::productCategory[1]/genericName");

1voto

Dimitre Novatchev Points 147842

Utilisez (en supposant que le nœud de contexte initial est soit une productCategory[genericName = 'DCinemaLens'] ou un productModel ) :

../preceding-sibling::*[1]

Vérification basée sur XSLT :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "/*/*/productCategory
            [genericName = 'DCinemaLens']
               /../preceding-sibling::*[1]"/>
-------------
<xsl:text/>
  <xsl:copy-of select=
  "/*/*/*/*/productModel/../preceding-sibling::*[1]"/>
 </xsl:template>
</xsl:stylesheet>

lorsque cette transformation est appliquée sur le document XML fourni :

<productCategory>
    <genericName>DigitalCinema</genericName>
    <productCategories>
        <productCategory>
            <genericName>DCinemaProj</genericName>
            <productModels>
                <productModel>ProjProd-1</productModel>
                <productModel>ProjProd-2</productModel>
                <productModel>ProjProd-3</productModel>
                <productModel>ProjProd-4</productModel>
            </productModels>
        </productCategory>
        <productCategory>
            <genericName>DCinemaLens</genericName>
        </productCategory>
    </productCategories>
</productCategory>

les deux éléments recherchés sont copiés sur la sortie :

<genericName>DigitalCinema</genericName>
-------------
<genericName>DCinemaProj</genericName>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X