使用local-name()在XSLT中获取第一个子节点

raf*_*ian 6 xml xslt xpath xalan xslt-1.0

假设我们有这个简单的xml ...

 <books>   
    <book>
       <author/>
       <title/>
    </book>
    <book>
       <author/>
       <title/>
    </book>
 </books>
Run Code Online (Sandbox Code Playgroud)

我正在使用此xpath来获取第一个book实例的元素.

//books[1]/*
Run Code Online (Sandbox Code Playgroud)

返回

<author/>
<title/>
Run Code Online (Sandbox Code Playgroud)

这工作正常,但我必须使用local-name().我试过以下但没有一个工作......

//*[local-name()='books']/*
Run Code Online (Sandbox Code Playgroud)

这会返回重复的作者和标题元素,不好,我只需要第一个孩子

//*[local-name()='books'][0]/*
Run Code Online (Sandbox Code Playgroud)

这不会返回任何东西

基本上,我想创建一个CSV文件,因此输出中的第一行将是一个标题,列出书籍属性名称,后跟任意数据值.我只需要让标题部分工作.

author,title
john,The End is Near
sally,Looking for Answers
Run Code Online (Sandbox Code Playgroud)

Dim*_*hev 12

这是一个FAQ - XPath []运算符的优先级(优先级)高于//伪运算符.

所以:

//someElemName[1]
Run Code Online (Sandbox Code Playgroud)

选择名称someElemName为其父元素的第一个子元素的每个元素- 并且,根据XML文档,可以有多个这样的元素.

要改变这一点,必须使用括号.

用途:

(//*[local-name() = 'book'])[1]/*
Run Code Online (Sandbox Code Playgroud)

另请注意:在XPath中,位置是从1开始的,而不是从0开始的.

基于XSLT的验证:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "(//*[local-name() = 'book'])[1]/*"/>
 </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

当此转换应用于以下XML文档时:

<books>
    <book num="1">
        <author num="1"/>
        <title num="1"/>
    </book>
    <book num="2">
        <author num="2"/>
        <title num="2"/>
    </book>
</books>
Run Code Online (Sandbox Code Playgroud)

选择所需节点并将其复制到输出:

<author num="1"/>
<title num="1"/>
Run Code Online (Sandbox Code Playgroud)


Bor*_*din 11

您说的路径表达式适合您

//books[1]/*
Run Code Online (Sandbox Code Playgroud)

生成任何<books>节点的第一个(并且仅在这种情况下)出现的所有子节点的列表.因为,在您的数据中,<books>的唯一出现位于根,它与之相同

/books/*
Run Code Online (Sandbox Code Playgroud)

它返回两个<book>节点,因此你说它只返回一个节点是错误的.

很难知道你需要什么,好像你总是申请local-name到根节点,然后你不需要知道它的名字,只需要访问它/*,所以你只需要

/*/*[1]
Run Code Online (Sandbox Code Playgroud)

但是,要在文档中的任何位置访问<books>节点的第一个子节点

//*[local-name()='books']/*[1]
Run Code Online (Sandbox Code Playgroud)

您应该小心地尽可能地限制您的上下文,因为启动XPath表达式//将强制搜索整个文档,如果相关节点始终位于根节点,这将是毫无意义且耗时的.