【问题标题】:Xpath 1.0 nodelist based on node names基于节点名称的 Xpath 1.0 节点列表
【发布时间】:2020-11-18 03:37:41
【问题描述】:

我不喜欢寻求帮助,但这次我完全被 xpath 查询卡住了。

请看一下这个 XML:

<doc>
  <car>
    <property id="color">
      <attribute id="black" />
      <attribute id="white" />
      <attribute id="green" />
    </property>
    <property id="size">
      <attribute id="small" />
      <attribute id="medium" />
      <attribute id="large" />
    </property>
  </car>
  <attributes>
    <color>white</color>
    <size>small</size>
  </attributes>
</doc>

应该根据属性节点名输出汽车/属性。期望的输出是:

<property id="color"><attribute id="white" /></property>
<property id="size"><attribute id="small" /></property>

xpath

/doc/car/property[@id=name(/doc/attributes/*)]/attribute[@id=/doc/attributes/*/text()]

只返回第一个节点,因为 name() 函数只返回第一个元素的名称。

谁能帮我找出一个有效的 xpath (XSLT 1.0)?非常感谢您提前提供的帮助!

【问题讨论】:

    标签: xpath


    【解决方案1】:

    您可以使用 XSLT-1.0 来实现这一点,但不仅仅是使用 XPath-1.0,因为在 XPath-1.0 中您只能返回第一项。这在 XSLT-1.0 中不是问题,因为您可以使用 xsl:for-each 循环,如下所示:

    <xsl:for-each select="/doc/attributes/*">
        <property id="{/doc/car/property[@id=current()/local-name()]/@id}"><attribute id="{/doc/car/property[@id=current()/local-name()]/attribute[@id=current()/.]/@id}" /></property>
    </xsl:for-each>
    

    此代码发出以下 XML:

    <property id="color"><attribute id="white"/></property>
    <property id="size"><attribute id="small"/></property>
    

    如所见,您的要求似乎有点多余,但我想您的更大方案证明了这种方法是合理的。

    【讨论】:

    • 事实上这是一个更大的场景。我把这个问题降到了最低限度。
    • 这很好。但相关的问题是:我的解决方案对您有帮助吗?如果没有,为什么不呢?
    • 一个有趣的方法,非常感谢!还要一两天我才能确定项目中的所有xpath表达式是否都可以替换。
    • :感谢您的思考。没错,我得离开xpath,通过XSL确定结果。使用 exsl:node-set() 我可以转换 RTF 结果。 XSL 解决方案看起来很简单。
    • @E.Wiest: 非常感谢您提供的三个样本。但无论父属性如何,它们都会选择任何匹配的属性。如果多个属性中出现相同的属性ID,则存在冲突。
    【解决方案2】:

    这些选项怎么样(我仍然不清楚您为什么使用name(),因为我在您的示例数据中没有看到任何命名空间):

    //property|//attribute[@id=//attributes/*]
    
    //attribute[@id=//attributes/*]|//attribute[@id=//attributes/*]/parent::property
    
    //property|//attribute[@id=substring-before(normalize-space(//attributes)," ") or @id=substring-after(normalize-space(//attributes)," ")]
    

    即使您必须在 attributes 节点内处理 @id 的命名空间,第三个选项也应该有效。

    输出:

    【讨论】:

      【解决方案3】:

      我的工作解决方案:

      <xsl:stylesheet version="1.0">
        <xsl:template match="/">
          <xsl:for-each select="/doc/car/property">
            <property id="{@id}"> 
              <xsl:variable name="id" select="@id" />
              <xsl:copy-of select="attribute[@id=/doc/attributes/*[name()=$id]/text()]" />
            </property>
          </xsl:for-each>
        </xsl:template>
      </xsl:stylesheet>
      

      【讨论】:

        【解决方案4】:

        另一种不使用循环的解决方案:

        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        
        <xsl:template match="/">
          <xsl:apply-templates select="doc/car/property"/>
        </xsl:template>
        
        <xsl:template match="property">
          <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:copy-of select="attribute[@id = /doc/attributes/*[name() = current()/@id]]"/>
          </xsl:copy>
        </xsl:template>
        </xsl:stylesheet>
        

        对于每个property,它都会复制元素节点及其属性。然后它复制其attribute 子代,其id/doc/attributes 下的相应元素匹配。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多