Получить узел предка с помощью PHP и xpath - PullRequest
4 голосов
/ 05 сентября 2011

Пожалуйста, прости, если следующее немного запутано, я убивал себя, пытаясь решить это.

Это кусок XML (экспортированный с очень большого сайта), который я использую для создания дерева категорий для мини-CMS. После того как у меня есть значение и имя узла, что не является проблемой, мне также нужно получить «родительский» каждый узел, то есть предшествующий ему узел, который находится над ним в иерархии.

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

Например, для productCategory-genericName DCinemaLens мне нужно иметь возможность захватить родителя как DigitalCinema и аналогично для отдельных productModel узлов где родитель будет DCinemaProj .

Я пробовал различные запросы в xpath, используя ancestor, previous-sibling и parent, и до сих пор не вижу, как получить нужный мне узел.

Вот мой код, так как он отказывается от моих попыток несколько минут назад.

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

    }

    }

У меня также был некоторый успех при получении информации в массиве, но он всегда просто снова возвращает каждое значение в XML.

$key = 'genericName';

    $derpgleep = $derp[$key];

    echo 'Derp= ' . $derpgleep;

    print_r($derp);

Надеюсь, есть действительно простое решение, которое я пропускаю. Я надеюсь, что я был ясен.

Ответы [ 2 ]

2 голосов
/ 05 сентября 2011

Используемое вами выражение XPath:

 ancestor::productCategory[1]/genericName

будет работать, если вы сможете выполнить это выражение, начиная с текущего узла.это действительно невозможно, начиная с текущего Array $genericName, так как он не содержит ни родителей, ни предков.

Я думаю, что вы можете перебрать все дерево XML.Это пример теста, который работает в соответствии с требованиями на основе вашего входного образца.

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

Это распечатает следующий фрагмент HTML:

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

Пока получить «родителя»из productModel вам нужен xpath, например:

        $derp = $xml->xpath("//productModel[.='" . 
            $productModel[0] . 
            "']/parent::productCategory[1]/genericName");
1 голос
/ 05 сентября 2011

Используйте (при условии, что начальный контекстный узел имеет значение productCategory[genericName = 'DCinemaLens'] или productModel):

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

Проверка на основе 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>

когда это преобразование применяется к предоставленному документу XML :

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

требуемые два элемента копируются на выход :

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