C# 文件内容的数据验证 C#如何使用JSON Schema或XML Schema验证文件结构

2次阅读

newtonsoft.json + jsonschema 验证需用 jobject.load() 加载 json 并调用 isvalid(),配合 jschema.parse() 解析 schema;须缓存线程安全的 jschema 实例,启用 path 获取 jsonpath 错误定位,避免反序列化绕过验证。

C# 文件内容的数据验证 C#如何使用JSON Schema或XML Schema验证文件结构

Newtonsoft.Json + JsonSchema 验证 JSON 文件结构

JSON Schema 验证在 C# 里不是 .NET 原生能力,得靠第三方库。目前最稳、文档最全、支持 Draft-04/06/07 的仍是 Newtonsoft.Json 生态里的 Newtonsoft.Json.Schema(注意:它已**停止维护**,但仍在大量生产项目中跑着;别用 NuGet 上标“preview”的新包,那是个半成品)。

常见错误是直接反序列化再手工比字段——这不算验证,只是“没崩”,漏掉类型错、必填项空、枚举越界等深层问题。

  • 安装时只加 Newtonsoft.JsonNewtonsoft.Json.Schema 两个包,别混 System.Text.Json 相关的验证逻辑,它们不兼容
  • Schema 文件必须是合法 JSON,且顶层是对象(不能是数组),否则 JSchema.Parse() 会抛 ArgumentException
  • 验证前先用 JObject.Load() 读文件,别用 JsonConvert.DeserializeObject<t>()</t>,后者会跳过 schema 检查直接转强类型
  • 示例片段:
    JObject data = JObject.Load(File.OpenText("input.json")); JSchema schema = JSchema.Parse(File.ReadAllText("schema.json")); IList<ValidationError> errors = new List<ValidationError>(); bool valid = data.IsValid(schema, out errors); if (!valid) { foreach (var e in errors) Console.WriteLine(e.ToString()); }

xml 文件用 XmlSchemaSet + XmlReaderSettings 做结构校验

.NET 自带的 XML Schema(XSD)验证足够可靠,关键在设置 XmlReaderSettings 时别漏掉 ValidationType = ValidationType.SchemaSchemas.Add(),否则等于没开验证。

容易踩的坑是把 XSD 文件路径写错,或 XSD 里用了 xs:import 却没把被引用的 schema 一并 Add() 进去,这时会报 XmlSchemaException:“无法解析引用的命名空间”。

  • 确保 XSD 文件编码为 UTF-8(无 bom),否则 XmlSchema.Read() 可能静默失败
  • 如果 XML 有命名空间,XSD 中的 targetNamespace 必须和 XML 的 xmlns 完全一致,字符大小写敏感
  • 验证时用 XmlReader.Create(stream, settings) 包一层,不要直接 XDocument.Load(),后者不触发 schema 校验
  • 示例关键行:
    var settings = new XmlReaderSettings { ValidationType = ValidationType.Schema }; settings.Schemas.Add("", "schema.xsd"); // 空字符串表示默认命名空间 settings.ValidationEventHandler += (s, e) => Console.WriteLine(e.Message); using var reader = XmlReader.Create("data.xml", settings);

别在运行时动态编译 Schema(尤其是 JSON)

Newtonsoft.Json.SchemaJSchema.Parse() 内部会做语法树构建和类型推导,对大 Schema(>50KB)或高频调用场景(如 API 网关层),每次 Parse 都是明显性能瓶颈。有人图省事写成单例缓存,结果发现并发JSchema 实例不是线程安全的,偶尔验证结果错乱。

  • 正确做法:启动时一次性 Parse() 并缓存 JSchema 实例,用 ConcurrentDictionary<string jschema></string> 按文件名或哈希索引
  • XML 的 XmlSchemaSet 是线程安全的,可以放心单例复用
  • 如果 Schema 来自用户上传,务必加长度限制(如 max 256KB)和超时(CancellationToken),防止恶意构造的嵌套 schema 导致溢出

验证失败时,错误信息够不够定位?

默认的 ValidationError(JSON)或 XmlSchemaValidationException(XML)只告诉你“哪一行错了”,但不说明“为什么错”。比如 "age": "abc" 在要求 Integer 的字段上,JSON Schema 错误信息可能是 “Expected integer, got string”,但不会指出是 $.user.profile.age 这个路径。

要拿到完整 JSONPath 或 XPath,必须手动遍历验证错误集合,拼接上下文。XML 方便些,异常里自带 LineNumberLinePosition;JSON 则得靠 ValidationError.Path 字段(需开启 JSchema.GenerationOptions = SchemaGenerationOptions.None 并确保原始 JSON 用 JObject 加载)。

  • JSON 场景下,检查 errors.select(e => e.Path) 是否为空——为空说明是顶层语法错(比如多逗号),不是结构错
  • XML 场景下,ValidationEventArgs.Exception.LineNumber 是字节流位置,不是文件行号;若 XML 有 DTD 或注释,实际行号可能偏移
  • 别依赖错误消息做自动化修复(比如自动转类型),验证环节只负责报错,清洗逻辑放前面

验证逻辑本身不难写,难的是让错误可追溯、性能不掉队、边界情况不崩——尤其当 Schema 来自外部或随配置热更时,路径处理、缓存生命周期、命名空间对齐这些点,一个没卡准,就变成线上排查黑洞。

text=ZqhQzanResources