【问题标题】:Xpath - select nodes depending on preceding-siblings and following-siblingsXpath - 根据前面的兄弟姐妹和后面的兄弟姐妹选择节点
【发布时间】:2016-11-09 07:10:05
【问题描述】:

我有一个这样的xml,

<doc>
    <p><c type="changeStart"/>​<style type="underline">text</style><c type="changeEnd"/><t/>In addition 
        to voting Finance Company and Business Company, Inc.: (i) the name 
        of the <c type="changeStart"/>new public entity<c type="changeEnd"/> will be “Finance Company.” 
        as Finance Company’s corporate existence is perpetual as opposed to Business Company, Inc.’s corprate 
        existence terminating <c type="changeStart"/>   <c type="changeEnd"/>
    </p>
</doc>

我需要选择存在于&lt;c type="changeStart"/&gt; 和&lt;c type="changeEnd"/&gt; 之间的节点。所以在上面的xml中应该选择以下节点,

  • ​&lt;style type="underline"&gt;text&lt;/style&gt;
  • new public entity
  • 两个空格 (' ')

我为此编写了以下 xpath,

//*[preceding-sibling::c[@type = 'changeStart'] and following-sibling::c[@type = 'changeEnd']][not(c [@type="changeStart"])]

但它没有选择正确的节点。任何建议如何修改我的 xpath 以选择我需要的内容?

【问题讨论】:

    标签: xml xpath xslt-2.0


    【解决方案1】:

    这是一种可能的 XPath:

    //node()[
        preceding-sibling::*[1][self::c/@type='changeStart']
            and
        following-sibling::*[1][self::c/@type='changeEnd']
    ]
    

    上面的 XPath 选择节点:

    • 直接在前面是&lt;c type="changeStart"/&gt;,和..
    • &lt;c type="changeEnd"/&gt; 直接跟随

    【讨论】:

    • 这只有在更改标记之间只有一个节点时才有效,对吗?
    • 我明白了。而且没有办法让它与中间的多个节点一起工作,是吗?
    • 据我所知,无法使用 XPath 1.0。 可能有一种使用 XSLT 2.0 的方法...
    • @Markus,是的,is 这样的 XPath 1.0 表达式——在我的回答中看到。
    【解决方案2】:

    使用这个 XPath 1.0 表达式(类似于@Flynn1179 的做法,但使用严格的&gt; 比较,并且也有排除节点的限制,其父节点也在更改的节点中,并且从结果中排除&lt;c&gt; 元素本身。此外,我使用的是preceding:: 轴而不是preceding-sibling:: 轴。这允许&lt;c&gt; 开始和结束类型元素位于文档中的不同级别(不是兄弟姐妹)):

     //node()
              [count(preceding::c[@type='changeStart']) 
               > count(preceding::c[@type='changeEnd'])
               and not(parent::*
                     [count(preceding::c[@type='changeStart']) 
                     > count(preceding::c[@type='changeEnd'])]
                     )
               and not(self::c[@type[.='changeStart' or .='changeEnd']])
              ]
    

    基于 XSLT 的验证:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
      <xsl:template match="/">
        <xsl:for-each select=
        "//node()
                  [count(preceding::c[@type='changeStart']) 
                   > count(preceding::c[@type='changeEnd'])
                   and not(parent::*
                         [count(preceding::c[@type='changeStart']) 
                         > count(preceding::c[@type='changeEnd'])]
                         )
                   and not(self::c[@type[.='changeStart' or .='changeEnd']])
                  ]">
       <xsl:value-of select="concat('&#xA;',position(), '. ')"/>
       <xsl:copy-of select="."/>
      </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时:

    <doc>
        <p><c type="changeStart"/><style type="underline">text</style><c type="changeEnd"/><t/>In addition 
            to voting Finance Company and Business Company, Inc.: (i) the name 
            of the <c type="changeStart"/>new public entity<c type="changeEnd"/> will be “Finance Company.” 
            as Finance Company’s corporate existence is perpetual as opposed to Business Company, Inc.’s corprate 
            existence terminating <c type="changeStart"/>   <c type="changeEnd"/>
        </p>
    </doc>
    

    产生正确的结果:

    1. <style type="underline">text</style>
    2. new public entity
    

    为了也得到带有两个空格的文本节点,请从上述转换中删除&lt;xsl:strip-space elements="*"/&gt; 声明。

    这是一个更具挑战性的案例。在这个 XML 文档上应用转换:

    <t>
      <x>
       <c type="changeStart"/>
        <y> content
          <z>
            <p>
    
            </p>
          </z>
        </y>
       <c type="changeStart"/>
        <v>
    
        </v>
       <c type="changeEnd"/>
       <r/>
       <c type="changeEnd"/>
       <s/>
      </x>
    </t>
    

    产生正确的结果:

    1. <y> content
          <z>
          <p/>
       </z>
    </y>
    2. <v/>
    3. <r/>
    

    【讨论】:

      【解决方案3】:

      这是另一种方法:

      //node()[
          count(preceding-sibling::c[@type='changeStart']) !=
          count((. | preceding-sibling::c)[@type='changeEnd'])
        ]
      

      但是,这在很大程度上取决于您的“changeStart”和“changeEnd”标记是否正确完成,包括开始和结束对。如果您可以保证会是这种情况,那么这应该接近您正在寻找的内容。

      【讨论】:

      • 好,+1。需要稍作修改——使用preceding:: 轴而不是preceding-sibling 并准确选择OP 想要的节点:没有&lt;c&gt; 元素,也没有选定元素的子节点。
      • 我猜你没有尝试过——它确实选择了 OP 需要的东西。老实说,鉴于标题是“根据前面的-siblings 和以下-siblings 选择节点”,我会坚持使用这些。
      • Flynn1179,我从这个答案中尝试了 XPath 表达式。在 This 的情况下,它会选择想要的节点。但在其他情况下,它会选择不需要的节点——例如 &lt;c&gt; 节点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多