简体   繁体   中英

Get ancestor node with PHP and xpath

Please forgive if the following is a bit muddled, I've been killing myself trying to work this out.

This is a chunk of XML (exported from a much large site) that I am using to create a category tree for a mini-CMS. Once I've got the value and name of the node, which is no problem, I also need to get the 'parent' each node, that is, the node preceding it which is above it in the hierarchy.

    <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>

For example, for the productCategory-genericName DCinemaLens , I need to be able to grab the parent as DigitalCinema , and similarly for the individual productModel nodes, where the parent would be DCinemaProj .

I've tried various different queries in xpath using ancestor, previous-sibling and parent and I still can't see to grab the node I need.

Here is my code as it stands from giving up on my attempts a few minutes ago.

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>';

    }

    }

I've also had some success getting information in an array, but it always just returns every value in the XML again.

$key = 'genericName';

    $derpgleep = $derp[$key];

    echo 'Derp= ' . $derpgleep;

    print_r($derp);

Hopefully there is a really easy solution I am overlooking. I hope I have been clear.

The XPath expression you are using:

 ancestor::productCategory[1]/genericName

would work if you were able to perform that expression starting from the current node. this is not possible indeed starting from the current Array $genericName as it does not contain parents, nor ancestors.

I think that your option is to re-traverse all the XML tree. This is a sample test which works as required, based on your input sample.

<?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";
    }
}
?>

This will print out the following HTML fragment:

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

While to get the "parent" of productModel you need an xpath like:

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

Use (supposing the initial context node is either a productCategory[genericName = 'DCinemaLens'] or a productModel ):

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

XSLT-based verification :

<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>

when this transformation is applied on the provided XML document :

<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>

the wanted two elements are copied to the output :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM