如何获得最深的XML元素

时间:2019-06-16 20:38:22

标签: php xml xpath

<Item>
<BrowseNodes>
    <BrowseNode>
        <BrowseNodeId>3024254031</BrowseNodeId>
        <Name>Tunnelzelte</Name>
        <Ancestors>
            <BrowseNode>
                <BrowseNodeId>3024250031</BrowseNodeId>
                <Name>Zelte</Name>
                <Ancestors>
                    <BrowseNode>
                        <BrowseNodeId>16435151</BrowseNodeId>
                        <Name>Camping & Outdoor</Name>
                        <Ancestors>
                            <BrowseNode>
                                <BrowseNodeId>16435121</BrowseNodeId>
                                <Name>Kategorien</Name>
                                <IsCategoryRoot>1</IsCategoryRoot>
                                <Ancestors>
                                    <BrowseNode>
                                        <BrowseNodeId>16435051</BrowseNodeId>
                                        <Name>Sport & Freizeit</Name>
                                    </BrowseNode>
                                </Ancestors>
                            </BrowseNode>
                        </Ancestors>
                    </BrowseNode>
                ´</Ancestors>
            </BrowseNode>
        </Ancestors>
    </BrowseNode>
</BrowseNodes>

嗨,

我尝试获取最深的“名称”元素和vlaue(在这种情况下为Sport&Freizeit),但不适用于xpath()

$temp=$item->xpath("//Name[last()]");

我想我不明白xpath()的工作原理。

有人暗示吗? “ BrowseNode”元素的数量是灵活的,因此我无法使用修复路径。

THX!

3 个答案:

答案 0 :(得分:2)

您必须包装//Name标签,然后获取最后一个项目。

(//Name)[last()]

答案 1 :(得分:2)

“最深”元素可以描述为没有Ancestors子节点的元素。

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

var_dump(
  $xpath->evaluate('string(//BrowseNode[not(Ancestors)]/Name)')
);
  • 获取任何“ BrowseNode”
    //BrowseNode
  • ...没有“祖先”子女
    //BrowseNode[not(Ancestors)]
  • ...获取其“名称”子级
    //BrowseNode[not(Ancestors)]/Name
  • ...将第一个获取的节点转换为字符串
    string(//BrowseNode[not(Ancestors)]/Name)

如果允许该结构具有多个BrowseNode分支,则仅靠Xpath无法解决问题。在这种情况下,您将必须将节点提取到数组中并对其进行排序。

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$leafs = array_map(
  function(DOMElement $node) use ($xpath) {
      // fetch name and level
      return [
          'name' => $xpath->evaluate('string(Name)', $node),
          'level' => (int)$xpath->evaluate('count(ancestor::BrowseNode)', $node),
          // keep the node for additional Xpath expressions
          'node' => $node
      ];  
  },
  // get BrowseNode elements without Ancestors
  iterator_to_array(
      $xpath->evaluate('//BrowseNode[not(Ancestors)]')
  )
);

// sort array by level descending
usort(
    $leafs,
    function($a, $b) {
        // compare level
        $result = -($a['level'] <=> $b['level']);
        // same level -> compare name
        return ($result !== 0) ? $result : strnatcmp($a['name'], $b['name']); 
    }
);

// let's output the leafs to see what has happened
foreach ($leafs as $leaf) {
    var_dump([$leaf['name'], $leaf['level']]);
}

通过将节点保留在叶数组中,可以使用其他Xpath表达式来获取相关数据。像所有祖先的名字一样:

// get the first 
if (count($leafs) > 0) {
    $leaf = $leafs[0];
    var_dump(
        array_map(
            function(DOMElement $node) {
                return $node->textContent;
            },
            // fetch Name of ancestors
            iterator_to_array(
                $xpath->evaluate('ancestor::BrowseNode/Name', $leaf['node'])
            )
        )
    );
}

答案 2 :(得分:0)

谢谢大家!我仍在寻找解决方案。到目前为止,我只得到空数组。我尝试过你的想法。

有人遇到了同样的问题:

Get Last XML Child from Tree of All Same Name Children

如果找到解决方案,我会发布它。