SaxonCS捕获XSLT转换错误疑问——Schematron校验失败断言未进入自定义错误列表
兄弟,我完全懂你的困惑!你现在遇到的核心问题是:Schematron的svrl:failed-assert属于校验结果的正常输出,并不是Saxon认定的「XSLT转换错误」,所以你的ErrorReporter根本不会捕获到这些内容。
为什么会这样?
Saxon的ErrorReporter负责捕获的是XSLT执行过程中出现的运行时异常——比如XSLT语法错误、节点查找失败、类型转换错误这类会中断或影响转换流程的问题。而Schematron的工作逻辑是:把校验规则编译成XSLT,然后通过转换XML生成包含failed-assert的SVRL报告,这整个过程是完全正常的XSLT转换,没有触发任何错误,自然不会被ErrorReporter记录。
怎么解决你的需求?
你要的是判断XML是否符合Schematron规则,其实不需要依赖ErrorReporter,直接处理转换出来的SVRL报告就可以了,这里给你两种实用方案:
方案一:解析SVRL输出文件,检查失败断言
这是最直接的方式,转换完成后读取生成的SVRL文件,用XPath查询是否存在svrl:failed-assert元素:
public static bool Validate(string schematronFilePath, string xmlFilePath) { Processor processor = new(); var compiler = processor.NewXsltCompiler(); Xslt30Transformer transformer = compiler.Compile(new Uri(schematronFilePath)).Load30(); transformer.SchemaValidationMode = SchemaValidationMode.Strict; XdmNode input = processor.NewDocumentBuilder().Build(new Uri(xmlFilePath)); transformer.GlobalContextItem = input; string svrlOutputPath = @"pathToOuputFile"; var outputFileStream = File.OpenWrite(svrlOutputPath); Serializer serializer = processor.NewSerializer(outputFileStream); transformer.ApplyTemplates(input, serializer); outputFileStream.Close(); // 加载SVRL输出文件并检查失败断言 XdmNode svrlDoc = processor.NewDocumentBuilder().Build(new Uri(svrlOutputPath)); XPathCompiler xpathCompiler = processor.NewXPathCompiler(); xpathCompiler.DeclareNamespace("svrl", "http://purl.oclc.org/dsdl/svrl"); XPathSelector failedAssertSelector = xpathCompiler.Compile("//svrl:failed-assert").Load(); failedAssertSelector.ContextItem = svrlDoc; // 如果存在失败断言,返回true(表示校验不通过) return failedAssertSelector.MoveNext(); }
方案二:修改Schematron编译后的XSLT,让失败断言触发错误
如果你希望把Schematron的失败断言当成Saxon的错误来捕获,可以修改Schematron生成的XSLT模板,让它在生成failed-assert的同时抛出错误。比如找到生成svrl:failed-assert的模板,添加saxon:error函数(需要引入Saxon命名空间):
<xsl:template match="..." mode="schematron-failed-assert"> <!-- 原有的生成failed-assert的代码 --> <svrl:failed-assert ...> ... </svrl:failed-assert> <!-- 添加这行,抛出错误 --> <saxon:error message="{svrl:text}" terminate="no"/> </xsl:template>
这样一来,每次生成failed-assert时,Saxon就会触发ErrorReporter,你的transformerErrorList就能捕获到对应的错误信息了。不过这种方式需要你能修改Schematron编译后的XSLT,或者自定义Schematron的编译规则。
总结
回到你的问题:你的代码本身没毛病,只是对Saxon的错误机制和Schematron的工作逻辑理解有偏差。最推荐用方案一,因为它更符合Schematron的原生设计,也不需要修改原有XSLT代码。
备注:内容来源于stack exchange,提问作者GrowNarko

