使用 XSLT 很容易做到这一点(无需在变量中捕获结果,或使用特殊的命名模板):
一、XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*/*">
<xsl:for-each select=
"Locality/text() | CollectorAndNumber/text()
| Institution/text() | Distribution/text()
| Note/text()
"
>
<xsl:value-of select="."/>
<xsl:if test="not(position() = last())">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
当此转换应用于以下 XML 文档时:
<root>
<record>
<Locality>Locality</Locality>
<CollectorAndNumber>CollectorAndNumber</CollectorAndNumber>
<Institution>Institution</Institution>
<Distribution>Distribution</Distribution>
<Note></Note>
<OtherStuff>Unimportant</OtherStuff>
</record>
</root>
产生了想要的结果:
Locality,CollectorAndNumber,Institution,Distribution
如果想要的元素不应该按文档顺序生成(问题中不需要,但由 Tomalak 提出),那么实现这一点仍然非常容易和优雅:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="porderedNames"
select="' CollectorAndNumber Locality Distribution Institution Note '"/>
<xsl:template match="/*/*">
<xsl:for-each select=
"*[contains($porderedNames, concat(' ',name(), ' '))]">
<xsl:sort data-type="number"
select="string-length(
substring-before($porderedNames,
concat(' ',name(), ' ')
)
)"/>
<xsl:value-of select="."/>
<xsl:if test="not(position() = last())">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
此处,所需元素的名称及其所需顺序在字符串参数中提供$porderedNames
,其中包含所有所需名称的空格分隔列表。
当上述转换应用于同一个 XML 文档时,会产生想要的结果:
CollectorAndNumber,Locality,Distribution,Institution
二、XSLT 2.0:
在 XSLT 中,这个任务更加简单(同样,不需要特殊函数):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*/*">
<xsl:value-of separator="," select=
"(Locality, CollectorAndNumber,
Institution, Distribution,
Note)[text()]" />
</xsl:template>
</xsl:stylesheet>
当将此转换应用于同一个 XML 文档时,会产生相同的正确结果:
Locality,CollectorAndNumber,Institution,Distribution
请注意,所需的元素将以任何所需的顺序生成,因为我们使用的是 XPath 2.0 序列类型(与 XSLT 1.0 解决方案中的并集相比),根据定义,它包含任何所需(指定)顺序的项目。