这个答案的主要贡献是一个可以与无限多种格式一起使用的解决方案(最后),只需在外部(全局)参数中指定所有“条目”替代名称$postElements
以及外部(全局)参数中的所有“发布日期”替代名称$pub-dateElements
.
除此之外,这里是如何指定选择所有的 XPath 表达式/rss//item
和所有/feed//entry
元素。
在只有两种可能的文档格式的简单情况下这个(由 @Josh Davis 提议)Xpath 表达式可以正确工作:
/rss//item | /feed//entry
更通用的 XPath 表达式允许从一组无限数量的文档格式中选择所需的元素:
/*[contains($topElements, concat('|',name(),'|'))]
//*[contains($postElements, concat('|',name(),'|'))]
其中变量$topElements
应替换为顶部元素的所有可能名称的管道分隔字符串,并且$postElements
应替换为“entry”元素的所有可能名称的管道分隔字符串。我们还允许“entry”元素在不同的文档格式中处于不同的深度。
特别是,对于这个具体情况,XPath 表达式将是:
/*[contains('|feed|rss|', concat('|',name(),'|'))]
//*[contains('|item|entry|', concat('|',name(),'|'))]
本文的其余部分展示了如何完全在 XSLT 中完成所需的完整处理——轻松而优雅。
I. A gentle介绍
使用 XSLT 进行此类处理既轻松又简单:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<myFeed>
<xsl:apply-templates/>
</myFeed>
</xsl:template>
<xsl:template match="channel|feed">
<xsl:apply-templates select="*">
<xsl:sort select="pubDate|published" order="descending"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="item|entry">
<post>
<xsl:apply-templates mode="identity"/>
</post>
</xsl:template>
<xsl:template match="pubDate|published" mode="identity">
<publicationDate>
<xsl:apply-templates/>
</publicationDate>
</xsl:template>
<xsl:template match="node()|@*" mode="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="identity"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
当此转换应用于此 XML 文档时(格式1):
<rss>
<channel>
<item>
<pubDate>2011-06-05</pubDate>
<title>Title1</title>
<description>Description1</description>
<link>Link1</link>
<author>Author1</author>
</item>
<item>
<pubDate>2011-06-06</pubDate>
<title>Title2</title>
<description>Description2</description>
<link>Link2</link>
<author>Author2</author>
</item>
<item>
<pubDate>2011-06-07</pubDate>
<title>Title3</title>
<description>Description3</description>
<link>Link3</link>
<author>Author3</author>
</item>
</channel>
</rss>
当它应用于此等效文件时(格式2):
<feed>
<entry>
<published>2011-06-05</published>
<title>Title1</title>
<description>Description1</description>
<link>Link1</link>
<author>Author1</author>
</entry>
<entry>
<published>2011-06-06</published>
<title>Title2</title>
<description>Description2</description>
<link>Link2</link>
<author>Author2</author>
</entry>
<entry>
<published>2011-06-07</published>
<title>Title3</title>
<description>Description3</description>
<link>Link3</link>
<author>Author3</author>
</entry>
</feed>
在这两种情况下,都会产生相同的想要的正确结果:
<myFeed>
<post>
<publicationDate>2011-06-07</publicationDate>
<title>Title3</title>
<description>Description3</description>
<link>Link3</link>
<author>Author3</author>
</post>
<post>
<publicationDate>2011-06-06</publicationDate>
<title>Title2</title>
<description>Description2</description>
<link>Link2</link>
<author>Author2</author>
</post>
<post>
<publicationDate>2011-06-05</publicationDate>
<title>Title1</title>
<description>Description1</description>
<link>Link1</link>
<author>Author1</author>
</post>
</myFeed>
二.完整的解决方案
这可以推广到参数化解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="postElements" select=
"'|entry|item|'"/>
<xsl:param name="pub-dateElements" select=
"'|published|pubDate|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="identity"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<myFeed>
<xsl:apply-templates select=
"//*[contains($postElements, concat('|',name(),'|'))]">
<xsl:sort order="descending" select=
"*[contains($pub-dateElements, concat('|',name(),'|'))]"/>
</xsl:apply-templates>
</myFeed>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test=
"contains($postElements, concat('|',name(),'|'))">
<post>
<xsl:apply-templates/>
</post>
</xsl:when>
<xsl:when test=
"contains($pub-dateElements, concat('|',name(),'|'))">
<publicationDate>
<xsl:apply-templates/>
</publicationDate>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="identity"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
此转换可用于无限多种格式,只需在外部(全局)参数中指定所有“条目”替代名称$postElements
以及外部(全局)参数中的所有“发布日期”替代名称$pub-dateElements
.
任何人都可以尝试此转换,以验证当应用于上面的两个 XML 文档时,它是否再次产生相同的、所需的正确结果。