XPath 通过嵌套选择节点(大多数嵌套节点减一)

Posted

技术标签:

【中文标题】XPath 通过嵌套选择节点(大多数嵌套节点减一)【英文标题】:XPath select nodes by nesting (most nested nodes minus one) 【发布时间】:2016-03-19 17:56:15 【问题描述】:

这是给定的xml:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Root>
    <WorkItem>
        <Id>716</Id>
        <WorkItemType>Product Backlog Item</WorkItemType>
        <TreeLevel>0</TreeLevel>
        <Children>
            <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>
                        </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>
                        </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>
                                        <Id>747</Id>
                                        <WorkItemType>Task</WorkItemType>
                                        <TreeLevel>4</TreeLevel>
                                        <Children />
                                    </WorkItem>
                                </Children>
                            </WorkItem>
                        </Children>
                    </WorkItem>
                    <WorkItem>
                        <Id>748</Id>
                        <WorkItemType>Task</WorkItemType>
                        <TreeLevel>2</TreeLevel>
                        <Children />
                    </WorkItem>
                    <WorkItem>
                        <Id>752</Id>
                        <WorkItemType>Product Backlog Item</WorkItemType>
                        <TreeLevel>2</TreeLevel>
                        <Children>
                            <WorkItem>
                                <Id>753</Id>
                                <WorkItemType>Product Backlog Item</WorkItemType>
                                <TreeLevel>3</TreeLevel>
                                <Children />
                            </WorkItem>
                        </Children>
                    </WorkItem>
                </Children>
            </WorkItem>
            <WorkItem>
                <Id>718</Id>
                <WorkItemType>Task</WorkItemType>
                <TreeLevel>1</TreeLevel>
                <Children />
            </WorkItem>
        </Children>
    </WorkItem>
</Root>

简化的xml是这样的

716 (PBI) Root
  - 717 (PBI)
    - 719 (PBI)
      - 721 (Task)
    - 720 (PBI)
      - 724 (Task)
    - 723 (Task)
      - 744 (Task)
        - 747 (Task)
    - 748 (Task)
    - 752 (PBI)
      - 753 (PBI)
  - 718 (Task)

我想检索 WorkItemType "Product Backlog Item" 的所有第二个内部节点。 我也可以说我想要“Product Backlog Items”,其中包含“Product Backlog Item”类型的子元素,但没有“Product Backlog Item”的子元素。

在给定的示例中,这将是

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

//WorkItem[WorkItemType[text()='Product Backlog Item'] and ./Children/WorkItem/WorkItemType[text() = 'Product Backlog Item'] and not(./Children//WorkItem/WorkItemType[text() = 'Product Backlog Item']/Children/WorkItem/WorkItemType[text() = 'Product Backlog Item'])]

为了更好的可读性

//WorkItem[WorkItemType[text()='Product Backlog Item'] 
and ./Children/WorkItem/WorkItemType[text() = 'Product Backlog Item'] 
and not(./Children//WorkItem/WorkItemType[text() = 'Product Backlog Item']/Children/WorkItem/WorkItemType[text() = 'Product Backlog Item'])]

通过这个 XPath 表达式,我得到了 ID 为 716、717 和 752 的项目。

但我只想要 752 项。

【问题讨论】:

你为什么不使用LINQ TO XML呢? @Sybren 感谢您的评论。我使用 xpath 是因为我将 xpath 表达式存储在配置文件中。以后需要扩展这个文件。据我所知LINQ TO XML 这是不可能的。 能否详细说明“第二个内部节点”? @YacoubMassad 感谢您的评论。我试图描述它。如果您检查简化的 xml 结构,我想选择 752。这是 PBI(产品待办事项)类型的第二个最内层节点。 我想我找到了解决这个查询问题的方法: //WorkItem[WorkItemType[text()='Product Backlog Item'] and Children/WorkItem/WorkItemType[text() = 'Product Backlog Item'] 而不是(Children/WorkItem/WorkItemType[text() != 'Product Backlog Item'])] 【参考方案1】:

我们不确切知道您的 XML 文档的结构有多少变化,但请尝试从完全不同的角度来看待它。

从最里面的WorkItem 开始,它有一个元素WorkItemType = 'Product Backlog Item',但没有任何子元素WorkItem。然后,使用parent:: 轴向后移动。

完整的 XPath 表达式如下所示

//WorkItem[WorkItemType = 'Product Backlog Item' and not(Children/WorkItem)]/parent::Children/parent::WorkItem

唯一的结果是

<WorkItem>
    <Id>752</Id>
    <WorkItemType>Product Backlog Item</WorkItemType>
    <TreeLevel>2</TreeLevel>
    <Children>
        <WorkItem>
            <Id>753</Id>
            <WorkItemType>Product Backlog Item</WorkItemType>
            <TreeLevel>3</TreeLevel>
            <Children/>
        </WorkItem>
    </Children>
</WorkItem>

请注意,上面的表达式要求内部 WorkItem 不包含任何其他 WorkItem 元素 - 这并不是您所要求的。如果我要非常仔细地遵循您的描述,这将导致我

//WorkItem[WorkItemType = 'Product Backlog Item' and not(Children/WorkItem[WorkItemType = 'Product Backlog Item'])]/parent::Children/parent::WorkItem

其中还包括 ID 为 717WorkItem

【讨论】:

以上是关于XPath 通过嵌套选择节点(大多数嵌套节点减一)的主要内容,如果未能解决你的问题,请参考以下文章

Python爬虫编程思想(58): 用Beautiful Soup CSS选择器嵌套选择节点

Python中Scrapy框架元素选择器XPath的简单实例

Python爬虫:想听榜单歌曲?使用BeautifulSoup库只需要14行代码即可搞定

Python爬虫:想听榜单歌曲?使用BeautifulSoup库只需要14行代码即可搞定

JQuery 在嵌套元素上查找没有选择器的文本节点仅适用于 .contents() 和 filter()

XPath语法