NET9中的JsonSchemaExporter

程序员有二十年 2024-08-26 15:50:35
.NET 9 中的 JsonSchemaExporterIntro

.NET 9 Preview 6 中引入了一个 JsonSchemaExporter,我们可以借助它根据类型来生成 json schema,之前我们有写过一篇文章使用 JsonSchema 来验证 API 的 response 使用 JsonSchema 验证 API 的返回格式,有了这个 API 之后就可以更方便地生成 JsonSchema 了

Samples

首先我们准备一下类型用以测试:

public Job{public int Id { get; set; }public string Title { get; set; } = string.Empty;public string? Description { get; set; }}

从 JsonSerializeOptions 获取类型的 json schema 结构

var type = typeof(Job);var defaultSchemaNode = JsonSchemaExporter.GetJsonSchemaAsNode( JsonSerializerOptions.Default, type );Console.WriteLine(JsonSerializer.Serialize(defaultSchemaNode, JsonSerializerOptions.Web));

我们可以使用 JsonSchemaExporter.GetJsonSchemaAsNode 来获取 jsonSchema,输出结果如下:

{"type":["object",""],"properties":{"Id":{"type":"integer"},"Title":{"type":"string"},"Description":{"type":["string",""]}}}

这个方法定义为扩展方法,我们也可以通过扩展方法的方式来使用,JsonSchema 导出之后是一个 JsonNode 对象,大小写命名规则等由 JsonSerializerOptions 来决定,所以需要一个 JsonSerializerOptions 参数,我们再来看下使用不同的 JsonSerializerOptions 的结果有何不同

var schemaNode = JsonSerializerOptions.Web.GetJsonSchemaAsNode(typeof(Job));Console.WriteLine(JsonSerializer.Serialize(schemaNode, JsonSerializerOptions.Web));

和前面相比,这次我们使用了 JsonSerializerOptions.Web, 会使用 CamelCase 的命名规则, 输出结果如下:

{"type":["object",""],"properties":{"id":{"type":["string","integer"],"pattern":"^-?(?:0|[1-9]\\d*)$"},"title":{"type":"string"},"description":{"type":["string",""]}}}

可以看到此时,我们的属性名成变成了小写,另外由于 Web option 默认允许字符串转成数值,所以能看到我们的 id,允许的 type 除了 integer 之外还有 string,不过 string 也多了一个数字的正则表达式规则校验,这也说明了 JsonSerializerOptions 对 jsonSchema 的影响是比较大的。

如果我想要 title 必填的话要怎么做呢,可以把 Title 设置为 required, 添加一个 required 修饰符即可

public Job{public int Id { get; set; }public required string Title { get; set; }public string? Description { get; set; }}

此时输出结果就变成了下面这样:

{"type":["object",""],"properties":{"id":{"type":["string","integer"],"pattern":"^-?(?:0|[1-9]\\d*)$"},"title":{"type":"string"},"description":{"type":["string",""]}},"required":["title"]}

可以看到在最后增加了一个 required 属性,里面有一个 title 表示 title 属性必填,没有的话 json schema 验证应该失败

除此之外,我们还可以在导出的时候做一些自定义的操作,示例如下:

var exporterOptions = new JsonSchemaExporterOptions{ TransformSchemaNode = (context, jsonNode) => {var node = jsonNode.DeepClone();var idNames = new[] { "id", "Id" };if (node["properties"] is not JsonObject propertiesNode)return node;foreach (var idName in idNames) {if (propertiesNode[idName] is JsonObject) {var requiredNode = node["required"];if (requiredNode is JsonArray jsonArrayNode) {var requiredProperties = JsonSerializer.Serialize(jsonArrayNode.Select(x => x.GetValue<string>()).Append(idName)); jsonArrayNode.ReplaceWith(JsonSerializer.Deserialize<JsonArray>(requiredProperties)); }else { node["required"] = JsonSerializer.Deserialize<JsonArray>($"""["{idName}"]"""); } } }return node; }};var schemaNode3 = JsonSerializerOptions.Web.GetJsonSchemaAsNode(typeof(Job), exporterOptions);Console.WriteLine(JsonSerializer.Serialize(schemaNode3, JsonSerializerOptions.Web));

这里我们在生成的 jsonSchema node 的基础之上,如果属性名称是 id 或者 Id 的话就将它添加到 required 中或者创建一个 required 并将 id 属性名添加进去,输出结果如下:

{"type":["object",""],"properties":{"id":{"type":["string","integer"],"pattern":"^-?(?:0|[1-9]\\d*)$"},"title":{"type":"string"},"description":{"type":["string",""]}},"required":["title","id"]}

这里可以看到针对前面的输出,required 里多个 id 属性

我们再来测试一下 Id 以及没有 required 属性的情况,我们将 required 修饰符给去掉,再加入 exporterOptions 和第一次的输出结果做个对比

var schemaNode4 = JsonSerializerOptions.Default.GetJsonSchemaAsNode(typeof(Job), exporterOptions);Console.WriteLine(JsonSerializer.Serialize(schemaNode4, JsonSerializerOptions.Web));

此时输出结果如下:

{"type":["object",""],"properties":{"Id":{"type":"integer"},"Title":{"type":"string"},"Description":{"type":["string",""]}},"required":["Id"]}

可以看到输出结果里有了 required, 再来用 json schema 验证下看看

这个示例只是为了说明可以自定义,实际使用可以直接添加一个 required 修饰符即可

More

目前的 JsonSchema 支持还比较早期,对于复杂的需求可能还需要自己扩展,比如说设置 schema 需要类似前面示例一样自己扩展下,在 .NET 10 里应该还会继续优化和增强

Referenceshttps://github.com/WeihanLi/SamplesInPractice/blob/main/net9sample/Net9Samples/JsonSample.cshttps://github.com/dotnet/runtime/issues/102788https://github.com/dotnet/runtime/pull/103322https://github.com/dotnet/runtime/issues/105769https://www.jsonschemavalidator.net/

0 阅读:0

程序员有二十年

简介:感谢大家的关注