1. 介绍
所以,你已经会把对象转成 JSON(和反过来)。但如果你的程序收到一些……奇怪的东西该怎么办?比如,你期望这样一个 JSON:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
但收到的是:
{
"name": 42,
"id": "not a number"
}
是的,C# 的序列化/反序列化器会试着把这些内容反序列化,但通常会导致运行时错误、数据丢失,甚至业务崩溃。在把数据继续传递、存库或通过网络发送之前,先确认数据是有效的 —— 否则你的代码就像没装保险的杂技演员。
这就是为什么需要验证:自动检查 JSON 是否符合规则 —— 值的类型、字段的必需性、范围、结构等等。
2. 有哪些 JSON 验证方式?
在 C# 和 .NET 里主要有三种做法:
- 自写验证逻辑:手动解析对象,写检查并在出错时抛异常。
- 在模型上用验证属性:比如来自 System.ComponentModel.DataAnnotations 的 [Required]、[Range]、[EmailAddress] 等。
- 用 JSON Schema 验证 — 今天的主角!
JSON Schema 是一个标准,用来形式化描述一个合法文档应该长什么样。Schema 本身也是 JSON。你可以指定哪些字段需要、字段类型、允许的值范围等。
通过 schema 你可以描述:
- 哪些字段必须存在。
- 期待的类型(string、array、number、object……)。
- 哪些字段是必需的,哪些不是。
- 数值范围(比如年龄从 0 到 150)。
- 模式、长度、枚举等。
最简单的 schema 示例
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
3. JSON Schema 的主要元素
我们通过例子看看 JSON Schema 里常见的关键字都是什么意思。
| 关键字 | 说明 | 示例 |
|---|---|---|
|
指向 JSON Schema 标准的版本 | |
|
值的类型(object, array, string, number 等) | |
|
对象属性的描述 | |
|
必需字段的数组 | |
|
数组元素的类型描述 | |
|
允许的值的枚举 | |
, |
数字的限制 | |
, |
字符串长度限制 | |
|
字符串的正则表达式 | |
可视化:一个表示人数组的 schema 示例
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
4. 在 C# 中如何把 JSON 跟 schema 校验
在 .NET 里没有内置的 JSON Schema 标准支持(截至 .NET 9)。实际项目通常用第三方库: NJsonSchema 或 Newtonsoft.Json.Schema (Json.NET Schema)。
安装 Newtonsoft.Json.Schema 包 (Json.NET Schema)
在终端(项目文件夹)里执行:
dotnet add package Newtonsoft.Json.Schema
重要: Newtonsoft.Json.Schema 是商业库(非商业用途免费)。对 pet 项目很合适。
用例:检查 JSON 是否匹配 schema
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
string schemaJson = @"{
'type': 'object',
'properties': {
'id': { 'type': 'integer' },
'name': { 'type': 'string' },
'email': { 'type': 'string', 'format': 'email' }
},
'required': ['id', 'name']
}";
// 假设我们有这样的 JSON
string json = @"{
'id': 123,
'name': 'Alice',
'email': 'alice@example.com'
}";
// 先解析 schema
JSchema schema = JSchema.Parse(schemaJson);
// 把 JSON 解析成 JToken
JToken jsonObj = JToken.Parse(json);
// 检查 JSON 是否符合 schema
bool valid = jsonObj.IsValid(schema, out IList<string> errors);
if (valid)
{
Console.WriteLine("JSON 有效!");
}
else
{
Console.WriteLine("JSON 无效!");
foreach (var error in errors)
Console.WriteLine(error);
}
如何在应用里使用验证?
通常流程:先验证(用 IsValid),再反序列化(用 JsonConvert.DeserializeObject<T>),然后才执行业务逻辑。这样可以在早期过滤掉脏数据。
if (jsonObj.IsValid(schema))
{
// 一切正常,可以反序列化
var person = JsonConvert.DeserializeObject<Person>(json);
}
else
{
// 停止处理!数据不合法。
}
5. JSON Schema 在真实场景下的应用
什么时候需要验证?
- 接收来自 API 的数据时(尤其是来自外部系统和不同客户端时)。
- 在微服务或数据库之间迁移数据时。
- 基于 schema 动态生成 UI 表单并自动填充时。
- 面试题:经常会被问到“如果收到了错误的 JSON 怎么办?”
给好奇者的有趣点
- 格式检查:"format":"email"、"date-time"。
- 组合规则:anyOf、oneOf、allOf。
- 嵌套对象和数组的验证。
- 根据需要扩展 schema。
6. 一个较大的实战示例
输入数据(用户列表):
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "chto eto?", "name": 123, "email": "not-an-email" }
]
Schema:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
在 .NET 里检查:
string jsonArray = @"[ ... ]"; // 见上面
string schemaJson = @"{ ... }"; // 见上面
JSchema schema = JSchema.Parse(schemaJson);
JToken arrayToken = JToken.Parse(jsonArray);
bool isValid = arrayToken.IsValid(schema, out IList<string> errs);
if (!isValid)
{
foreach (var error in errs)
Console.WriteLine(error);
// 可能的错误示例:
// "String 'chto eto?' is not a valid integer."
// "Integer 123 is not a valid string."
// "String 'not-an-email' is not a valid email address."
}
7. 使用 JSON Schema 时常见错误
错误 #1:schema 和实际数据不同步。 修改模型后忘记更新 schema。验证要么放行错误,要么拒绝正确的数据。
错误 #2:schema 类型和模型类型不匹配。 模型里的字段类型换了(比如 id 变成了字符串),而 schema 还写着 integer —— 验证会报错。
错误 #3:缺少对业务关键字段的必需性声明。 字段可能没被标记为必需,但业务逻辑依赖它,缺失会导致故障。
错误 #4:数据格式不正确。 字符串看起来像 email 或日期,但可能不合法。使用 format 或额外检查。
错误 #5:所用库滞后于标准。 标准在发展,库不一定跟得上。有些检查可能缺失或行为不同。
GO TO FULL VERSION