XSLT中的双斜杠

时间:2014-03-04 15:03:30

标签: xml xslt xpath

我有一个样式表,它带有两个文件,一个来自工程仓库,第二个来自文档仓库,并将它们合并以创建一些DITA文件(进一步处理)。最近,我尝试将文档文件的内容拆分为通用文件和特定文件。因此,我的合并现在是一个包含两个文档文件的工程文件。

通用文件原样:

<?xml version="1.0" encoding="UTF-8"?>
<messages xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <message id="IDENT_STRING">
    ....
  </message>
</messages>

特定文件的ENTITY标记指向通用文件:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE messages [
<!ENTITY generic-file SYSTEM "generic-file.xml">
]>
<messages> &generic-file; <!-- specific-file -->
  <message id="IDENT_STRING2">
    ....
  </message>
</messages>

选择是这样写的:

<xsl:copy-of select="$docid/message[@id=$id]/doc/explanation/text()"/>

这只会抓取特定文件中的内容。直到我将select更改为两个斜杠才能使我的样式表正常工作。这是正确的版本:

<xsl:copy-of select="$docid//message[@id=$id]/doc/explanation/text()"/>

我对社区的问题是1)为什么第二种语法是正确的? 2)我怎样才能更快地找到它?

2 个答案:

答案 0 :(得分:4)

这是关于xpath的一个很好的信息来源,/ x / y // z是什么,http://www.w3.org/TR/xpath/#location-paths

从缩写语法部分: //是/ descendant-or-self :: node()/的缩写。例如,// para是/ descendant-or-self :: node()/ child :: para的缩写,因此将选择文档中的任何para元素(甚至作为文档元素的para元素将由/选择) / para,因为document element节点是根节点的子节点); div // para是div / descendant-or-self :: node()/ child :: para的缩写,因此将选择div children的所有para后代。

//是在xpath的开头还是中间,它的意思是相同的。

就学习这些东西而言,对我来说,我必须构建我试图转换的xml的小型人工xml或简化shell,然后运行xslt。在Visual Studio中有一个相当方便的方法,在2005年左右这样做,我认为它仍然在那里,虽然我有一段时间没有搞砸它。我发现MSDN或w3.org是一个很好的资源,虽然w3上的语言有时可能有点消化。

使用此xml:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <a id="a1">
        <d id="1" />
        <d id="2" />
    </a>
    <a id="a2">
        <d id="3" />
        <d id="4" />
    </a>
    <b>
        <c id="1" />
        <c id="2" />
    </b>
</root>

使用此xslt:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <test>
            <xsl:apply-templates/>
        </test>
    </xsl:template>

    <xsl:template match="b">
        <z>
            <select>//a</select>
            <xsl:copy-of select="//a"/>
        </z>
        <z>
            <select>.//a</select>
            <xsl:copy-of select=".//a"/>
        </z>
        <z>
            <select>.//c</select>
            <xsl:copy-of select=".//c"/>
        </z>
    <z>
        <select>/root/a/d</select>
        <xsl:copy-of select="/root/a/d"/>
    </z>
    <z>
            <select>/root/a</select>
            <xsl:copy-of select="/root/a"/>
        </z>
        <z>
            <question>so what is the node?</question>
            <period>
                <xsl:copy-of select="."/>
            </period>
            <slash>
                <xsl:copy-of select="/"/>
            </slash>
        </z>
    </xsl:template>
</xsl:stylesheet>

获得此结果:

<?xml version="1.0" encoding="utf-8"?>
<test>
  <z>
    <select>//a</select>
    <a id="a1">
      <d id="1" />
      <d id="2" />
    </a>
    <a id="a2">
      <d id="3" />
      <d id="4" />
    </a>
  </z>
  <z>
    <select>.//a</select>
  </z>
  <z>
    <select>.//c</select>
    <c id="1" />
    <c id="2" />
  </z>
  <z>
    <select>/root/a/d</select>
    <d id="1" />
    <d id="2" />
    <d id="3" />
    <d id="4" />
  </z>
  <z>
    <select>/root/a</select>
    <a id="a1">
      <d id="1" />
      <d id="2" />
    </a>
    <a id="a2">
      <d id="3" />
      <d id="4" />
    </a>
  </z>
  <z>
    <question>so what is the node?</question>
    <period>
      <b>
        <c id="1" />
        <c id="2" />
      </b>
    </period>
    <slash>
      <root>
        <a id="a1">
          <d id="1" />
          <d id="2" />
        </a>
        <a id="a2">
          <d id="3" />
          <d id="4" />
        </a>
        <b>
          <c id="1" />
          <c id="2" />
        </b>
      </root>
    </slash>
  </z>
</test>

因此,当构造一个xpath时,//将获得该节点类型的后代,如果你使用//开始一个xpath,你将转到该文档。

如果你开始。然后你用当前节点开始你的xpath(如果你使用xsl:apply-templates,当前节点是xsl:template中匹配后的任何节点,但是如果你使用xsl:call-template,那么当前节点是相同的作为您创建xsl:call-template的当前节点。

如果您使用/开始使用xpath,那么您将引用文档的根目录。

大概$ docId是一个引用一个文档或另一个文档的节点集,用于设置xpath的起点,因此$ docId // message意味着获取节点集中所有$ docId的消息元素。 / p>

最后你需要//的原因是你没有完全指定路径。在我提供的示例中,我构造了一个/ root / a / d,它是d元素的完整路径,它将所有4个元素拉出。 $ docId //消息只允许您查找低于$ docId节点集根目录的任何元素“消息”。有时候你可能没有一个干净的对称来获取记录:例如,如果你有/ messages / critical / message和/ messages / warning / message,使用/ messages //消息的xpath或者更方便//消息来获得你需要的东西。

我希望这会有所帮助。

答案 1 :(得分:1)

如果没有看到$docid变量的内容,则很难诊断出您的问题。但我会试一试。

我认为这个变量包含类似的东西:

<xsl:variable name="docid" select="document('generic-file.xml')"/>

现在,//element检索元素(在这种情况下,名称为“element”的元素),无论它们在文档树中的位置 - 更重要的是,无论在哪里在树上。换句话说,以//开头的表达式不依赖于上下文。

所以,以下

<xsl:copy-of select="$docid//message[@id=$id]/doc/explanation/text()"/>

表示:从外部文档中检索所有message元素,无论它们的id属性是否对应某个变量$id,它们的位置都是如此。然后,查找子元素doc及其子元素explanation并复制后者的文本节点。


另一方面,这个

<xsl:copy-of select="$docid/message[@id=$id]/doc/explanation/text()"/>

表示:从外部文档中检索一个元素,该元素是 root 元素,称为“message”。但显然,在XML中没有根元素message。那里的根元素是messages。所以这可能有效:

<xsl:copy-of select="$docid/messages/message[@id=$id]/doc/explanation/text()"/>

/的表达式取决于执行它的上下文。


更详细。下面的转换说明虽然模板匹配c元素(因此上下文为c),但可以使用a检索//元素。

<强>输入

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <a/>
    <b>
        <c/>
    </b>
</root>

<强>样式表

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" indent="yes"/>
   <xsl:strip-space elements="*"/>

   <xsl:template match="/">
      <xsl:apply-templates/>
   </xsl:template>

   <xsl:template match="c">
       <xsl:copy-of select="//a"/>
   </xsl:template>

</xsl:stylesheet>

<强>输出

<?xml version="1.0" encoding="UTF-8"?>
<a/>

如果模板更改为以下内容:

<xsl:template match="c">
       <xsl:copy-of select=".//a"/>
</xsl:template>

没有输出。