我有这样的XML:
<node id="1">
<data alias="Show">ShowName1</data>
<data alias="Dates">21/04/2009,23/04/2009,27/04/2009,</data>
</node>
<node id="2">
<data alias="Show">ShowName2</data>
<data alias="Dates">22/04/2009,25/04/2009,29/04/2009,</data>
</node>
它有X个节点,每个节点都有一个节目名称,以及一串逗号分隔的节目日期。 我可以对节目日期进行标记,但我想制作一个排序列表,其中包含所有节目的所有节目日期,按日期排序。像这样:
<shows>
<show>
<name>ShowName1</name>
<date>21/04/2009</date>
</show>
<show>
<name>ShowName2</name>
<date>22/04/2009</date>
</show>
<show>
<name>ShowName1</name>
<date>23/04/2009</date>
</show>
<show>
<name>ShowName2</name>
<date>25/04/2009</date>
</show>
<show>
<name>ShowName1</name>
<date>27/04/2009</date>
</show>
<show>
<name>ShowName2</name>
<date>29/04/2009</date>
</show>
</shows>
这有可能吗?
答案 0 :(得分:2)
要仅使用一个转换过程执行此操作,您需要将新创建的展示元素视为节点集,以便模板可以使用内部 xsl:sort 机制。 XSL Transformations (XSLT) Version 1.0 spec没有将自创元素转换为节点集的方法。但是,MSXML parser(3.0或更高版本)确实有一个扩展来提供该功能。此外,有一个exslt extension我认为一些较新版本的Firefox支持允许将变量动态交换到节点集。但无论如何,这是一个使用MSXML解析器正常工作的样式表。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:variable name="shows">
<xsl:apply-templates select="//data[@alias='Dates']"/>
</xsl:variable>
<shows>
<xsl:apply-templates select="msxsl:node-set($shows)//show">
<xsl:sort select="substring(date, 7, 4)"/><!-- year -->
<xsl:sort select="substring(date, 4, 2)"/><!-- month -->
<xsl:sort select="substring(date, 1, 2)"/><!-- day -->
</xsl:apply-templates>
</shows>
</xsl:template>
<xsl:template match="show">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="data[@alias='Dates']">
<xsl:call-template name="eachDate">
<xsl:with-param name="node" select=".."/>
<xsl:with-param name="dates" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="eachDate">
<xsl:param name="node" select="."/>
<xsl:param name="dates" select="''"/>
<xsl:if test="string-length($dates)">
<show>
<name><xsl:value-of select="$node/data[@alias='Show']/text()"/></name>
<date><xsl:value-of select="substring-before($dates, ',')"/></date>
</show>
<xsl:call-template name="eachDate">
<xsl:with-param name="node" select="$node"/>
<xsl:with-param name="dates" select="substring-after($dates, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:1)
由于输入XML的形状相当糟糕,我们必须跳过一些箍来获得你想要的东西。
正如幸运先生已在他的回答中所述,我们必须首先将文件转换为更有用的临时形式。然后,更有用的临时表单将被扩展函数转换回节点集,并再次处理以产生所需的结果。
在我的回答中,我将使用修改后的身份转换来实现以下临时形式:
<root>
<node id="1">
<data alias="Show">ShowName1</data>
<data alias="Dates">
<date sort="20090421">21/04/2009</date>
<date sort="20090423">23/04/2009</date>
<date sort="20090427">27/04/2009</date>
</data>
</node>
<node id="2">
<data alias="Show">ShowName2</data>
<data alias="Dates">
<date sort="20090422">22/04/2009</date>
<date sort="20090425">25/04/2009</date>
<date sort="20090429">29/04/2009</date>
</data>
</node>
</root>
使用此输入,使用@sort
元素的<date>
属性可以直接排序。
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl "
>
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
<!-- prepare our temporary form (a "result tree fragment") -->
<xsl:variable name="rtf">
<xsl:apply-templates mode="rtf" />
</xsl:variable>
<!-- transform the result tree fragment back to a node-set -->
<xsl:variable name="doc" select="msxsl:node-set($rtf)" />
<!-- transform the temporary node-set, sorted by date -->
<shows>
<xsl:apply-templates select="$doc//date">
<xsl:sort select="@sort" />
</xsl:apply-templates>
</shows>
</xsl:template>
<xsl:template match="date">
<show show_id="{ancestor::node/@id}">
<name><xsl:value-of select="../../data[@alias='Show'][1]/text()" /></name>
<date><xsl:value-of select="." /></date>
</show>
</xsl:template>
<!-- all following templates are for producing the temporary form only -->
<xsl:template match="@*|node()" mode="rtf">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="rtf" />
</xsl:copy>
</xsl:template>
<xsl:template match="data[@alias='Dates']" mode="rtf">
<xsl:copy>
<!-- copy all attributes -->
<xsl:apply-templates select="@*" mode="rtf" />
<!-- this produces the <date> elements -->
<xsl:call-template name="tokenize-datelist" />
</xsl:copy>
</xsl:template>
<xsl:template name="tokenize-datelist">
<xsl:param name="input" select="." />
<xsl:param name="delim" select="','" />
<xsl:variable name="temp" select="concat($input, $delim)" />
<xsl:variable name="head" select="substring-before($temp, $delim)" />
<xsl:variable name="tail" select="substring-after($input, $delim)" />
<xsl:if test="$head != ''">
<date>
<!-- this produces the @sort attribute -->
<xsl:call-template name="tokenize-date">
<xsl:with-param name="input" select="$head" />
</xsl:call-template>
<xsl:value-of select="$head" />
</date>
<xsl:if test="$tail != ''" >
<xsl:call-template name="tokenize-datelist">
<xsl:with-param name="input" select="$tail" />
<xsl:with-param name="delim" select="$delim" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="tokenize-date">
<xsl:param name="input" select="''" />
<xsl:param name="delim" select="'/'" />
<xsl:variable name="dd" select="substring-before($input, $delim)" />
<xsl:variable name="my" select="substring-after($input, $delim)" />
<xsl:variable name="mm" select="substring-before($my, $delim)" />
<xsl:variable name="yy" select="substring-after($my, $delim)" />
<xsl:attribute name="sort">
<xsl:value-of select="concat($yy, $mm, $dd)" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
当通过msxsl.exe(XSLT 1.0处理器)运行时,会生成以下输出:
<shows>
<show show_id="1">
<name>ShowName1</name>
<date>21/04/2009</date>
</show>
<show show_id="2">
<name>ShowName2</name>
<date>22/04/2009</date>
</show>
<show show_id="1">
<name>ShowName1</name>
<date>23/04/2009</date>
</show>
<show show_id="2">
<name>ShowName2</name>
<date>25/04/2009</date>
</show>
<show show_id="1">
<name>ShowName1</name>
<date>27/04/2009</date>
</show>
<show show_id="2">
<name>ShowName2</name>
<date>29/04/2009</date>
</show>
</shows>
答案 2 :(得分:1)
<强>予。使用FXSL 1.x的XSLT 1.0解决方案
此转化:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext"
>
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/*">
<xsl:variable name="vDates">
<xsl:for-each select="node">
<nodeData name="{data[@alias = 'Show']}">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="data[@alias = 'Dates']"/>
<xsl:with-param name="pDelimiters"
select="','"/>
</xsl:call-template>
</nodeData>
</xsl:for-each>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vDates)/*/*[text()]">
<xsl:sort data-type="number" select="substring(.,7)"/>
<xsl:sort data-type="number" select="substring(.,4,2)"/>
<xsl:sort data-type="number" select="substring(.,1,2)"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="word">
<show>
<name>
<xsl:value-of select="../@name"/>
</name>
<date>
<xsl:value-of select="."/>
</date>
</show>
</xsl:template>
</xsl:stylesheet>
应用于提供的“XML文档”,更正为格式良好(人们何时会学习提供格式良好的XML文档? < / em>很难?):
<t>
<node id="1">
<data alias="Show">ShowName1</data>
<data alias="Dates">21/04/2009,23/04/2009,27/04/2009,</data>
</node>
<node id="2">
<data alias="Show">ShowName2</data>
<data alias="Dates">22/04/2009,25/04/2009,29/04/2009,</data>
</node>
</t>
产生想要的结果:
<show>
<name>ShowName1</name>
<date>21/04/2009</date>
</show>
<show>
<name>ShowName2</name>
<date>22/04/2009</date>
</show>
<show>
<name>ShowName1</name>
<date>23/04/2009</date>
</show>
<show>
<name>ShowName2</name>
<date>25/04/2009</date>
</show>
<show>
<name>ShowName1</name>
<date>27/04/2009</date>
</show>
<show>
<name>ShowName2</name>
<date>29/04/2009</date>
</show>
<强> II。一种可能的XSLT 2.0解决方案:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:variable name="vAllData" as="xs:string+">
<xsl:for-each select="node">
<xsl:variable name="vName" select="data[@alias='Show']"/>
<xsl:for-each select=
"tokenize(data[@alias='Dates'], ',')[.]">
<xsl:value-of select="concat($vName, '+',.)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$vAllData">
<xsl:sort data-type="number" select=
"substring(substring-after(.,'+'),7)"/>
<xsl:sort data-type="number" select=
"substring(substring-after(.,'+'),4,2)"/>
<xsl:sort data-type="number" select=
"substring(substring-after(.,'+'),1,2)"/>
<show>
<name>
<xsl:value-of select="substring-before(.,'+')"/>
</name>
<date>
<xsl:value-of select="substring-after(.,'+')"/>
</date>
</show>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
当对同一文档应用上述XSLT 2.0转换时,会产生相同的正确结果。