2014-03-04 51 views
0

我有一个样式表需要两个文件,一个来自工程回购库,另一个来自文档回购库,并合并它们以创建一些DITA文件(进一步处理)。最近,我试图将文档文件的内容分割成通用文件和特定文件。因此,我的合并现在是一个具有两个文档文件的工程文件。XSLT中的双斜杠

通用文件,因为它是:

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

的具体文件有一个实体标签的通用文件指出:

<?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()"/> 

这只会抓取特定文件中的内容。直到我改变选择有两个斜杠,我的样式表正常工作。这是正确的版本:

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

我对社区的问题是1)为什么第二个语法正确? 2)我如何更迅速地发现它?

回答

2

下面是关于XPath该/ X/Y // z是什么样的信息的良好来源,http://www.w3.org/TR/xpath/#location-paths

从缩写语法部分: //短的/后裔或自身::节点()/。例如,// para是/ descendant-or-self :: node()/ child :: para的缩写,因此将选择文档中的任何para元素(即使是作为文档元素的para元素也将被//para,因为文档元素节点是根节点的子节点); div // para是div/descendant-or-self :: node()/ child :: para的缩写,因此将选择div子元素的所有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> 

得到这样的结果:构建一个XPath时

<?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(如果你使用xsl:apply-templates,当前节点就是在xsl:template匹配后的任何事情,但是如果你使用xsl:call-template,那么当前节点是相同的作为您创建xsl:call-template的当前节点)。

如果你用/来启动xpath,那么你引用了你的文档的根目录。

推测$ docId是一个节点集,它指向一个文档或另一个文档,它设置了xpath的起始点,所以$ docId // message表示获取节点集中所有$ docId的消息元素。

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

我希望这会有所帮助。

+0

这些都是很好的答案 - 很难选择哪一个才是答案。非常感谢。 –

1

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

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

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

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

因此,下面

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

指:从外部文档中检索无论他们在哪里,如果他们的id属性对应于某一变$id所有message元素。然后,查找子元素doc及其子元素explanation并复制后者的文本节点。


在另一方面,本

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

指:从外部文件检索是根元件和被称为“信息”的元素。但显然,在你的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/> 

如果模板被改变到t他如下:

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

没有输出。

+2

//从任何你所在的地方得到所有后代......而不是从文档中的任何地方。如果你做/ x/y [@ id = $ id] // z你只会得到那个特定y下的z而不是每个z都到处 – Felan

+0

@Felan'select =“// z”'的行为(选择* * all ** z)不同于'select =“y // z”'(在y下选择z)。第二种情况就是你所描述的,而第一种情况就是我所说的,我假设它出现在OP的代码中。你错误地使用了''后代::'轴的''''。 –

+0

// z从任何点都选择所有z。行为上,无论是在顶部还是在中部,它都是相同的。所有关于上下文的讨论都令人困惑,在我看来,这是令人误解的。如果你的xml的结构是z/y/x/v/u,你的xpath是/ z/y,那么你将得到所有你的x和v,如果你的xpath是// u,那么你无论z,y's,x's和v是什么,都可以获得所有的u。 – Felan