使用C# Saxon API校验XML与XSD时无法捕获全部验证错误
解决Saxon API一次性捕获所有XML验证错误的问题
核心原因
Saxon默认错误处理机制会在遇到第一个严重错误时终止验证流程,想要收集全部错误,需要自定义错误监听器来接管错误处理逻辑,避免验证中断。
解决方案步骤
1. 自定义错误监听器
实现IErrorListener接口,将所有验证错误(包括警告、普通错误、致命错误)存储到列表中,关键是不要在错误处理方法中抛出异常,否则会中断验证:
using Saxon.Api; using System.Collections.Generic; public class CollectingErrorListener : IErrorListener { public List<XmlValidationIssue> Issues { get; } = new List<XmlValidationIssue>(); public void Error(StaticError error) { Issues.Add(new XmlValidationIssue(error.Message, error.LineNumber, error.ColumnNumber, error.ErrorCode, IssueType.Error)); } public void FatalError(StaticError error) { Issues.Add(new XmlValidationIssue(error.Message, error.LineNumber, error.ColumnNumber, error.ErrorCode, IssueType.FatalError)); } public void Warning(StaticError error) { Issues.Add(new XmlValidationIssue(error.Message, error.LineNumber, error.ColumnNumber, error.ErrorCode, IssueType.Warning)); } } public enum IssueType { Warning, Error, FatalError } public class XmlValidationIssue { public string Message { get; } public int LineNumber { get; } public int ColumnNumber { get; } public string ErrorCode { get; } public IssueType Type { get; } public XmlValidationIssue(string message, int lineNumber, int columnNumber, string errorCode, IssueType type) { Message = message; LineNumber = lineNumber; ColumnNumber = columnNumber; ErrorCode = errorCode; Type = type; } }
2. 修改校验代码,接入自定义监听器
在创建SchemaValidator后,替换默认的错误监听器为自定义实现,执行验证后即可获取所有错误:
using Saxon.Api; using System.IO; public void ValidateXml(string xmlFilePath, string xsdFilePath) { // 初始化Saxon处理器 var processor = new Processor(); var schemaManager = processor.SchemaManager; // 加载XSD Schema using var xsdStream = File.OpenRead(xsdFilePath); schemaManager.Load(new XmlTextReader(xsdStream)); // 创建验证器并绑定自定义错误监听器 var validator = schemaManager.NewSchemaValidator(); var errorCollector = new CollectingErrorListener(); validator.ErrorListener = errorCollector; // 加载待验证的XML using var xmlStream = File.OpenRead(xmlFilePath); var xmlReader = processor.NewXmlReader(xmlStream); validator.SetSource(xmlReader); // 执行验证(不会因错误中断) validator.Run(); // 处理收集到的所有问题 if (errorCollector.Issues.Count > 0) { foreach (var issue in errorCollector.Issues) { Console.WriteLine($"[{issue.Type}] 行{issue.LineNumber}, 列{issue.ColumnNumber}: {issue.Message} (错误码: {issue.ErrorCode})"); } // 可根据业务需求抛出汇总异常,或返回错误列表 throw new InvalidOperationException($"XML验证失败,共检测到{errorCollector.Issues.Count}个问题"); } else { Console.WriteLine("XML验证通过"); } }
3. 关键注意事项
- 自定义监听器的
Error和FatalError方法绝对不能抛出异常,否则Saxon会立刻终止验证,无法收集后续错误。 - 可通过
IssueType区分问题级别,按需过滤或处理不同类型的验证问题。 - 确保使用的Saxon版本支持自定义
ErrorListener(HE/PE/EE版本均支持,建议使用较新的稳定版本)。
示例测试文件
示例XSD(test.xsd)
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Root"> <xs:complexType> <xs:sequence> <xs:element name="Name" type="xs:string" minOccurs="1"/> <xs:element name="Age" type="xs:int" minOccurs="1"/> <xs:element name="Email" type="xs:anyURI" minOccurs="1"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
示例XML(test.xml,含多个错误)
<?xml version="1.0" encoding="UTF-8"?> <Root> <Age>abc</Age> <!-- 数据类型错误 --> <!-- 缺少Name元素 --> <Email>invalid-email</Email> <!-- URI格式错误 --> <ExtraElement>多余内容</ExtraElement> <!-- 未定义元素 --> </Root>
运行校验代码后,会一次性捕获全部4个验证问题。
内容的提问来源于stack exchange,提问作者Rhishikesh Savadikar
相关产品推荐
相关产品推荐

