简体   繁体   中英

PHP/XPath: Iterating through ancestor nodes in XML using SimpleXML

I need to loop through the Ancestors nodes in order to get corresponding BrowseNodeId and Name values. How do I do this using XPath? I'm trying something like the the following, but it's not working:

//BrowseNode[1]//Ancestors[1]/BrowseNode/BrowseNodeId/text()
//BrowseNode[1]//Ancestors[1]/BrowseNode/Name/text()

I think the //Ancestors part of the XPath is actually searching the entire XML tree. What I need to do is visit each node to get the needed values. How do I do this? In the example below, I want: Europe -> History -> Subjects -> Books.

Example XML:

    <BrowseNode>
        <BrowseNodeId>4952</BrowseNodeId>
        <Name>France</Name>
        <Ancestors>
            <BrowseNode>
                <BrowseNodeId>4935</BrowseNodeId>
                <Name>Europe</Name>
                <Ancestors>
                    <BrowseNode>
                        <BrowseNodeId>9</BrowseNodeId>
                        <Name>History</Name>
                        <Ancestors>
                            <BrowseNode>
                                <BrowseNodeId>1000</BrowseNodeId>
                                <Name>Subjects</Name>
                                <IsCategoryRoot>1</IsCategoryRoot>
                                <Ancestors>
                                    <BrowseNode>
                                        <BrowseNodeId>283155</BrowseNodeId>
                                        <Name>Books</Name>
                                    </BrowseNode>
                                </Ancestors>
                            </BrowseNode>
                        </Ancestors>
                    </BrowseNode>
                </Ancestors>
            </BrowseNode>
        </Ancestors>
    </BrowseNode>

In the example below, I want: Europe -> History -> Subjects -> Books.

I think you need:

//Ancestors/BrowseNode/Name

Or just iterate over //Ancestors/BrowseNode results and get the Name and BrowseNodeId children with any DOM method.

$browse_nodes = $xml->BrowseNode;
foreach ($browse_nodes as $browse_node) {
    echo $browse_node->BrowseNodeId.nodeValue;
    echo $browse_node->Name.nodeValue;
}

Maybe try to locate the final node, then extract is ancestors ?

<?php

$xml = "
    <BrowseNode>
        <BrowseNodeId>4952</BrowseNodeId>
        <Name>France</Name>
        <Ancestors>
            <BrowseNode>
                <BrowseNodeId>4935</BrowseNodeId>
                <Name>Europe</Name>
                <Ancestors>
                    <BrowseNode>
                        <BrowseNodeId>9</BrowseNodeId>
                        <Name>History</Name>
                        <Ancestors>
                            <BrowseNode>
                                <BrowseNodeId>1000</BrowseNodeId>
                                <Name>Subjects</Name>
                                <IsCategoryRoot>1</IsCategoryRoot>
                                <Ancestors>
                                    <BrowseNode>
                                        <BrowseNodeId>283155</BrowseNodeId>
                                        <Name>Books</Name>
                                    </BrowseNode>
                                </Ancestors>
                            </BrowseNode>
                        </Ancestors>
                    </BrowseNode>
                </Ancestors>
            </BrowseNode>
        </Ancestors>
    </BrowseNode>
";


$sxe = simplexml_load_string($xml);

// Set the target to reach
$target = current($sxe->xpath("//BrowseNode[./BrowseNodeId = '283155']"));

// Get all names from root to target
$names = array_map('strval', $target->xpath('ancestor-or-self::Ancestors/BrowseNode/Name'));
print_r($names);
// Array
// (
//     [0] => Europe
//     [1] => History
//     [2] => Subjects
//     [3] => Books
// )

// Get all ids from root to target
$ids = array_map('strval', $target->xpath('ancestor-or-self::Ancestors/BrowseNode/BrowseNodeId'));
print_r($ids);
// Array
// (
//     [0] => 4935
//     [1] => 9
//     [2] => 1000
//     [3] => 283155
// )

// Final
$combined = array_combine($ids, $names);
print_r($combined);
// Array
// (
//     [4935] => Europe
//     [9] => History
//     [1000] => Subjects
//     [283155] => Books
// )
/BrowseNode/Ancestors//BrowseNode/Name/text()

Will work with you sample.

However, it will search all nodes descandant to the "first" Ancestors element. Reversing and looking from the deepest BrowseNode would do pretty much the same.

In XSLT the viable and necessary alternative would be using keys for better efficiency.

I may have misunderstood your description of precisely what you want, but hopefully not. This snippet looks for all of the "top level" BrowseNode elements and loops over them, getting their associated ancestor BrowseNode elements each time.

// Get top level BrowseNode elements
$browse_nodes = $xml->xpath('//BrowseNodes/BrowseNode');
foreach ($browse_nodes as $browse_node) {
    // Get ancestor BrowseNode elements
    $ancestors = $browse_node->xpath('Ancestors//BrowseNode');
    // Do whatever you like with them! :-)
}

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