【问题标题】:Return nodes that have siblings that start-with substring of self返回具有以 self 的子字符串开头的兄弟节点的节点
【发布时间】:2022-01-25 15:07:40
【问题描述】:

我正在尝试在 Excel 的 FILTERXML() 函数中提出一个干净的 Xpath 1.0 表达式来返回具有以下要求的节点:

  • 节点必须有一个兄弟节点(以下),其开头的三个字符完全相同。

关键是要找出数据中是否存在某种程度的相似性。想象一下以下示例数据:

<t>
    <s>ABCDEF</s>
    <s>GHIJKL</s>
    <s>MNOPQR</s>
    <s>GHISTU</s>
    <s>ABVWXY</s>
</t>

从这里,我想返回 GHIJKL,因为它的前 3 个字符“GHI”位于倒数第二个节点的开头

我一直在尝试将starts-with()substring()count() 之类的函数拼凑在一起,但未能正确完成。我的(显然是错误的)尝试:

//s[count(following::*[starts-with(., substring(<placeholder>, 1,3))]>0]

我不确定是否有可能写什么而不是占位符如何修改查询以告诉我想要的表达式取每个节点最左边的三个字符,测试后面的是否有重复。

【问题讨论】:

    标签: xml xpath xpath-1.0


    【解决方案1】:

    以下表达式会起作用吗?

    //s[substring(., 1, 3) = following::*/substring(., 1, 3)]
    

    【讨论】:

    • 是的。这会奏效。但不幸的是,这需要作者无法使用的 XPath-2.0 功能。
    • @zx485 就在这里。它看起来很有希望,但不会在 xpath 1.0 中让步。不过感谢您的建议。
    • @JvdV 发布了另一种 XPath 解决方案作为解决方法(仅需要少量内容替换)。 - 亚历山德拉的帖子很好; 遗憾的是,FilterXML 和 MSXML2 中还没有 XPath 2.0 功能
    【解决方案2】:

    要求(干净的 Xpath 1.0 表达式):节点必须有一个兄弟节点(以下),其开头的三个字符完全相同。

    据我了解您的帖子,您只想获取实际具有满足此要求的追随者的节点内容。

    解决方法可能是在每个 s 节点中包含缩写属性 a 并使用以下 XPath 表达式:

        "//*[substring(.,1,3)=following-sibling::*/@a]"
    

    为了使这个可重现,我扩展了原始 xml content 并添加了一个数字后缀以轻松识别结果(当然这需要事先替换,我把重点放在一边主要问题,并保持简短)

    Sub GetNodesHavingSimilarFollowers()
    'a) Define wellformed xml content
        Dim content As String
        content = _
        "<t>" & _
            "<s a='ABC'>ABCDEF1</s>" & _
            "<s a='GHI'>GHIJKL2</s>" & _
            "<s a='MNO'>MNONot3</s>" & _
            "<s a='GHI'>GHINot4</s>" & _
            "<s a='ABV'>ABVNot5</s>" & _
            "<s a='ABC'>ABCPQR6</s>" & _
            "<s a='ABC'>ABCSTU7</s>" & _
            "<s a='ABC'>ABCNot8</s>" & _
        "</t>"
    '~~~~~~~~~~~~~~~~~~~~~~~~~~
    'b) Define XPath expression
    '~~~~~~~~~~~~~~~~~~~~~~~~~~
        Dim XPth As String
        XPth = "//*[substring(.,1,3)=following-sibling::*/@a]"
    
    'c) Execute FilterXML
        Dim x: x = Application.FilterXML(content, XPth)
        Select Case VarType(x)
            Case vbError
                Debug.Print "Error: ", x
            Case vbString
                Debug.Print x, "1 element only."
            Case Else
                Debug.Print Join(Application.Transpose(x), "|"), UBound(x) & " elements found."
    End Select
    
    End Sub
    
    

    VB 编辑器的即时窗口中的示例输出

    由于节点 3 和 5 是纸牌,而节点 4 和 8 (尽管显示的缩写与之前的其他节点相同) 没有直接追随者,因此只有四个元素左:

    ABCDEF1|GHIJKL2|ABCPQR6|ABCSTU7           4 elements found.
    

    【讨论】:

    • 谢谢@T.M.非常聪明。这是我会牢记的策略,但是我想避免一开始就进行任何返工。我很好奇我们是否可以通过调整 XML 的 xpath 转向来做到这一点。但随着时间的推移,我怀疑这会发生。
    • 我不太相信 FilterXML 以可靠的方式支持节点和节点兄弟之间的这些同时子字符串比较的所有可能变体。例如,像"//*[substring(.,1,3)=substring(following::*,1,3)]" 这样的 XPath 测试提供了输出,但是对于令人满意且逻辑正确的输出似乎存在内部限制。 @JvdV
    • 这将更接近您的需求:在 xml 节点值排序的条件下(更准确地说:至少根据前 3 个字符排序),您可以执行以下 XPath 表达式: "//*[substring(.,1,3)=substring(following-sibling::*,1,3)]" 对应于将每个节点与其直接邻居进行比较(与使用"//*[substring(.,1,3)=substring(following-sibling::*[1],1,3)]" 相同) - @JvdV `
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    相关资源
    最近更新 更多