闽公网安备 35020302035485号
public class Job
{
// 堆代码 duidaima.com
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","null"],"properties":{"Id":{"type":"integer"},"Title":{"type":"string"},"Description":{"type":["string","null"]}}}
这个方法定义为扩展方法,我们也可以通过扩展方法的方式来使用,JsonSchema 导出之后是一个 JsonNode 对象,大小写命名规则等由 JsonSerializerOptions 来决定,所以需要一个 JsonSerializerOptions 参数,我们再来看下使用不同的 JsonSerializerOptions 的结果有何不同var schemaNode = JsonSerializerOptions.Web.GetJsonSchemaAsNode(typeof(Job)); Console.WriteLine(JsonSerializer.Serialize(schemaNode, JsonSerializerOptions.Web));和前面相比,这次我们使用了 JsonSerializerOptions.Web, 会使用 CamelCase 的命名规则, 输出结果如下:
{"type":["object","null"],"properties":{"id":{"type":["string","integer"],"pattern":"^-?(?:0|[1-9]\\d*)$"},"title":{"type":"string"},"description":{"type":["string","null"]}}}
可以看到此时,我们的属性名成变成了小写,另外由于 Web option 默认允许字符串转成数值,所以能看到我们的 id,允许的 type 除了 integer 之外还有 string,不过 string 也多了一个数字的正则表达式规则校验,这也说明了 JsonSerializerOptions 对 jsonSchema 的影响是比较大的。如果我想要 title 必填的话要怎么做呢,可以把 Title 设置为 required, 添加一个 required 修饰符即可public class Job
{
public int Id { get; set; }
public required string Title { get; set; }
public string? Description { get; set; }
}
此时输出结果就变成了下面这样:{"type":["object","null"],"properties":{"id":{"type":["string","integer"],"pattern":"^-?(?:0|[1-9]\\d*)$"},"title":{"type":"string"},"description":{"type":["string","null"]}},"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;
// 堆代码 duidaima.com
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","null"],"properties":{"id":{"type":["string","integer"],"pattern":"^-?(?:0|[1-9]\\d*)$"},"title":{"type":"string"},"description":{"type":["string","null"]}},"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","null"],"properties":{"Id":{"type":"integer"},"Title":{"type":"string"},"Description":{"type":["string","null"]}},"required":["Id"]}
可以看到输出结果里有了 required, 再来用 json schema 验证下看看。
这个示例只是为了说明可以自定义,实际使用可以直接添加一个 required 修饰符即可。