XPath根据类型条件选择子节点,对于特定类型,选择其所有子节点

本文关键字:选择 类型 子节点 于特定 XPath 条件 | 更新日期: 2023-09-27 18:26:22

这是我给定的XML:

<WorkItem>
    <Id>717</Id>
    <WorkItemType>Product Backlog Item</WorkItemType>
    <TreeLevel>1</TreeLevel>
    <Children>
        <WorkItem>
            <Id>719</Id>
            <WorkItemType>Product Backlog Item</WorkItemType>
            <TreeLevel>2</TreeLevel>
            <Children>
                <WorkItem>
                    <Id>721</Id>
                    <WorkItemType>Task</WorkItemType>
                    <TreeLevel>3</TreeLevel>
                    <Children>
                        <WorkItem>
                            <Id>722</Id>
                            <WorkItemType>Task</WorkItemType>
                            <TreeLevel>4</TreeLevel>
                            <Children />
                        </WorkItem>
                    </Children>
                </WorkItem>
            </Children>
        </WorkItem>
        <WorkItem>
            <Id>720</Id>
            <WorkItemType>Product Backlog Item</WorkItemType>
            <TreeLevel>2</TreeLevel>
            <Children>
                <WorkItem>
                    <Id>724</Id>
                    <WorkItemType>Task</WorkItemType>
                    <TreeLevel>3</TreeLevel>
                    <Children>
                        <WorkItem>
                            <Id>726</Id>
                            <WorkItemType>Task</WorkItemType>
                            <TreeLevel>4</TreeLevel>
                            <Children />
                        </WorkItem>
                    </Children>
                </WorkItem>
                <WorkItem>
                    <Id>725</Id>
                    <WorkItemType>Task</WorkItemType>
                    <TreeLevel>3</TreeLevel>
                    <Children>
                        <WorkItem>
                            <Id>727</Id>
                            <WorkItemType>Task</WorkItemType>
                            <TreeLevel>4</TreeLevel>
                            <Children />
                        </WorkItem>
                        <WorkItem>
                            <Id>728</Id>
                            <WorkItemType>Task</WorkItemType>
                            <TreeLevel>4</TreeLevel>
                            <Children />
                        </WorkItem>
                        <WorkItem>
                            <Id>729</Id>
                            <WorkItemType>Task</WorkItemType>
                            <TreeLevel>4</TreeLevel>
                            <Children>
                                <WorkItem>
                                    <Id>745</Id>
                                    <WorkItemType>Task</WorkItemType>
                                    <TreeLevel>5</TreeLevel>
                                    <Children />
                                </WorkItem>
                                <WorkItem>
                                    <Id>746</Id>
                                    <WorkItemType>Task</WorkItemType>
                                    <TreeLevel>5</TreeLevel>
                                    <Children />
                                </WorkItem>
                            </Children>
                        </WorkItem>
                    </Children>
                </WorkItem>
            </Children>
        </WorkItem>
        <WorkItem>
            <Id>723</Id>
            <WorkItemType>Task</WorkItemType>
            <TreeLevel>2</TreeLevel>
            <Children>
                <WorkItem>
                    <Id>744</Id>
                    <WorkItemType>Task</WorkItemType>
                    <TreeLevel>3</TreeLevel>
                    <Children />
                </WorkItem>
            </Children>
        </WorkItem>
    </Children>
</WorkItem>

我想检索具有两个特定类型的子节点的节点的所有节点。如果这种类型是一种,我想得到所有的后代。

我试图用这个xpath命令接收它(我在C#中使用System.Xml.XmlDocument):

xmlDoc.SelectNodes("Children/WorkItem[WorkItemType[text()='Product Backlog Item']]|Children/WorkItem[WorkItemType[text()='Task']]/following::WorkItem[WorkItemType[text()='Task']]");
// splitted for better readability
Children/WorkItem[
    WorkItemType[
        text()='Product Backlog Item']
    ]|
    Children/WorkItem[
        WorkItemType[
           text()='Task']
    ]/following::WorkItem[WorkItemType[text()='Task']]

这只为我提供了ID为719和720的节点。但是我期望具有Id的WorkItem节点:719和720(xpath表达式的第一部分)以及723和744(来自第二个xpath表达式)。

  • 我的目标是拥有WorkItemType"Product Backlog Item"的WorkItem元素,该元素具有WorkItemType的"Product Backlog Item"answers"Task"的子WorkItem元素。
    • 如果是"Task"类型的WorkItem元素,我希望下面有所有子WorkItem元素

我如何用XPath表达这一点?

在给定的XML中,我期望ID为719720723744 的WorkItem元素

XPath根据类型条件选择子节点,对于特定类型,选择其所有子节点

目前还不清楚您想要实现什么,但我将尝试向您展示一个处理此任务的可能解决方案。

在我看来,创建一个巨大的XPath表达式并不重要,因为几年后它可能真的很难理解。对于您的情况,不可能在一个XPath表达式中获得您想要的所有内容。你可以把它分开:

foreach (var element in xmlDoc.SelectNodes("WorkItem/Children").OfType<XmlElement>())
{
    var elements = element.SelectNodes("WorkItem[WorkItemType[text()='Product Backlog Item' or text()='Task']]");
    foreach(var child in elements.OfType<XmlElement>())
         Process(child);                
}
public void Process(XmlElement rootElement)
{
    // Print some info about the work item
    // ...
    foreach (var element in rootElement.SelectNodes("Children").OfType<XmlElement>())
    {
        // I'm not sure whether it's exactly what you want but you can 
        // easily change this expression.
        var children = element.SelectNodes("WorkItem[WorkItemType[text()='Task']]");
        // continue the processing of children
        foreach (var child in children.OfType<XmlElement>())
            Process(child);
    }
}

首先,检查根子级的WorkItems的所有节点,然后将它们发送到Process方法,该方法也称为内部(递归)。它将帮助您处理更深层的层次结构。

我找到了一个适用于我想做的事情的xpath:

Children/WorkItem[WorkItemType[text()='Product Backlog Item']]|Children/WorkItem[WorkItemType[text()='Task']]/descendant-or-self::WorkI‌​tem[WorkItemType[text()='Task']]

更好的可读性

Children/WorkItem[WorkItemType[
                  text()='Product Backlog Item']
                 ]|
         Children/WorkItem[WorkItemType[
                           text()='Task']
                          ]/descendant-or-self::WorkI‌​tem[WorkItemType[
                                                         text()='Task']]