【发布时间】:2015-03-20 20:32:05
【问题描述】:
我有一个使用文档并输出 SOAP 消息的 XSLT 样式表,其中正文采用 WCF 数据协定(此处未指定)定义的特定格式。问题是 WCF 对什么是“字母”顺序有一个特殊的概念,并认为以下顺序是正确的:
- 交流
- 抗体
这是因为它在内部使用了序号字符串比较。细节并不有趣,可以说 XSLT <sort> 本身不支持这种排序,但是为了将格式可能不同的输入文档转换为可接受的 SOAP 消息,样式表必须能够对输出元素进行排序根据这个特殊的顺序。因此,我决定在脚本块中实现节点排序。这是 C# 解决方案的一部分,使用 XslCompiledTransform,因此 msxsl:script 可用。
给定一个样式表:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="urn:functions"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl exsl"
xmlns:exsl="http://exslt.org/common"
>
<msxsl:script implements-prefix="fn" language="C#">
<![CDATA[
public class OrdinalComparer : IComparer
{
public int Compare(object x, object y)
{
return string.CompareOrdinal((string)x, (string)y);
}
}
public XPathNodeIterator OrdinalSort(XPathNavigator source)
{
var query = source.Compile("/*");
query.AddSort(source.Compile("local-name()"), new OrdinalComparer());
return source.Select(query);
}
]]>
</msxsl:script>
<xsl:template match="Stuff">
<xsl:element name="Body">
<xsl:element name="Request">
<xsl:variable name="sort">
<xsl:apply-templates select="*"/>
</xsl:variable>
<xsl:for-each select="fn:OrdinalSort($sort)">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
还有一个输入文件:
<?xml version='1.0' encoding='utf-8'?>
<Root>
<Stuff>
<Age></Age>
<AIS></AIS>
<Something></Something>
<BMI></BMI>
</Stuff>
</Root>
我希望输出对最里面的元素进行如下排序:
- AIS
- 年龄
- 体重指数
- 某事
这不会发生。相反,元素是按照它们进入的顺序发出的。在样式表执行时对其进行调试,我可以看到调用了 OrdinalSort 函数,它返回的迭代器确实以所需的顺序枚举元素,但是 XSLT 处理器以某种方式忽略了这一点并按照遇到的顺序发出元素。
我还验证了在控制台应用程序中解析文档并运行相同的迭代器查询会以正确的顺序发出元素。
为什么,我能做些什么呢?我目前唯一的预感是 XSLT 引擎将迭代器的父导航器(它与传递给排序函数的内容没有改变)解释为要重现的元素,并忽略迭代器的内容。
【问题讨论】:
标签: c# xml visual-studio xslt soap