我有一个样式表,它带有两个文件,一个来自工程仓库,第二个来自文档仓库,并将它们合并以创建一些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)我怎样才能更快地找到它?
答案 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>
没有输出。