XPath按类型条件选择子节点,并为某些类型选择所有子节点

时间:2015-12-11 11:42:20

标签: c# xml xpath nodes

这是我给出的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的节点。 但我希望WorkItem节点的Id为:719和720(xpath表达式的第一部分)以及723和744(来自第二个xpath表达式)。

  • 我的目标是让WorkItemType的WorkItem元素'Product Backlog Item'具有Children WorkItem-WorkItemType'Product Backlog Item'的元素以及'Task'。
    • 如果是'Task'类型的WorkItem元素,我想在下面有所有子WorkItem元素。

如何在XPath中表达这一点?

在给定的XML中,我期望ID为719,720,723,744的WorkItem元素

2 个答案:

答案 0 :(得分:0)

目前还不清楚你想要实现什么,但我会尽力向你展示一个可能的解决方案来处理这项任务。

在我看来,它并没有创造出一个巨大的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方法,该方法也称为内部(递归)。它将帮助您处理更深层次的结构。

答案 1 :(得分:0)

我找到了一个适用于我想做的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']]