在.NET Core(.NET 5)中基于XSLT 2.0文件实现Schematron验证的可行方案咨询
嘿,我来帮你梳理这个问题——首先得纠正你一个小误解:Saxon其实已经完全支持.NET Core/.NET 5及以上版本了!最新的SaxonCS是专门针对.NET平台开发的原生版本,支持XSLT 2.0/3.0,这应该是最省心的方案,先给你讲这个。
方案1:用SaxonCS直接跑你的XSLT 2.0文件
这是最优解,不需要修改现有XSLT,直接就能在.NET 5里用。
首先通过NuGet安装SaxonCS:
Install-Package SaxonCS
然后写一段简单的C#代码调用它执行验证(假设你的XSLT是用来输出验证错误信息的):
using Saxon.Api; using System.IO; using System.Xml; // 初始化Saxon处理器 var processor = new Processor(); var compiler = processor.NewXsltCompiler(); // 加载你的XSLT 2.0文件 var xsltExecutable = compiler.Compile(new Uri(@"C:\path\to\your\validation-rules.xsl")); var transformer = xsltExecutable.Load(); // 加载要验证的XML文档 var inputXml = new XmlDocument(); inputXml.Load(@"C:\path\to\your\input.xml"); var inputNode = processor.NewDocumentBuilder().Build(inputXml); transformer.InitialContextNode = inputNode; // 捕获验证输出结果 using var sw = new StringWriter(); var serializer = processor.NewSerializer(sw); transformer.Run(serializer); // 处理验证结果(比如打印错误或保存到文件) var validationResult = sw.ToString(); Console.WriteLine(validationResult);
方案2:把XSLT 2.0降级为XSLT 1.0(如果必须用原生.NET)
如果因为某些限制不能用第三方库,那得手动把XSLT 2.0的特性转换成1.0的写法,这里列举几个常见的转换点:
1. 替换XSLT 2.0专属函数
像tokenize()、matches()、replace()这类2.0函数,XSLT 1.0本身不支持,但可以用EXSLT的扩展库(http://exslt.org/strings),或者自己写递归模板实现:
- 比如2.0的
tokenize($text, '\s+'),换成1.0+EXSLT的写法:
<!-- 先引入EXSLT命名空间 --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" extension-element-prefixes="str"> <!-- 使用str:tokenize替代原生tokenize --> <xsl:variable name="split-text" select="str:tokenize($text, ' ')"/> </xsl:stylesheet>
2. 替换分组逻辑
XSLT 2.0的xsl:for-each-group要换成1.0里的Muenchian分组:
- 2.0写法:
<xsl:for-each-group select="item" group-by="@category"> <group category="{current-grouping-key()}"> <xsl:copy-of select="current-group()"/> </group> </xsl:for-each-group>
- 1.0 Muenchian分组写法:
<!-- 先定义分组键 --> <xsl:key name="item-by-category" match="item" use="@category"/> <!-- 遍历每个分组的第一个节点 --> <xsl:for-each select="item[generate-id() = generate-id(key('item-by-category', @category)[1])]"> <group category="{@category}"> <!-- 输出当前分组的所有节点 --> <xsl:copy-of select="key('item-by-category', @category)"/> </group> </xsl:for-each>
3. 处理序列和多值变量
XSLT 2.0支持序列(比如$vals = ('a','b','c')),但1.0只能处理节点集或单个值。你可以把序列转换成虚拟节点集,比如:
<xsl:variable name="vals"> <val>a</val> <val>b</val> <val>c</val> </xsl:variable> <!-- 使用exsl:node-set把树转换成节点集 --> <xsl:variable name="vals-set" select="exsl:node-set($vals)/val" xmlns:exsl="http://exslt.org/common"/>
4. 移除强类型声明
XSLT 2.0的强类型(比如xs:int、xs:string)在1.0里不存在,需要去掉类型声明,改用number()这类函数做类型转换。
方案3:调用外部命令行工具
如果不想引入.NET库,还可以调用Saxon的Java命令行版本(Saxon-HE是免费的),通过.NET的Process类执行命令并捕获输出:
using System.Diagnostics; var process = new Process(); process.StartInfo.FileName = "java"; // 假设你已经下载了Saxon-HE的jar包 process.StartInfo.Arguments = "-jar saxon-he-12.4.jar -s:C:\\path\\to\\input.xml -xsl:C:\\path\\to\\your-xslt2.xsl -o:C:\\path\\to\\validation-result.xml"; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.UseShellExecute = false; process.Start(); var output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); // 处理输出结果 Console.WriteLine(output);
这个方案需要安装Java环境,适合对.NET依赖要求非常严格的场景。
内容的提问来源于stack exchange,提问作者S. Aziz Kazdal

