Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Klasa JsonSchemaExporter, wprowadzona na platformie .NET 9, umożliwia wyodrębnianie dokumentów schematu JSON z typów platformy .NET przy użyciu wystąpienia lub JsonSerializerOptions. Wynikowy schemat zawiera specyfikację kontraktu serializacji JSON dla typu .NET. Schemat opisuje kształt tego, co byłoby serializowane i co można zdeserializować.
Poniższy fragment kodu przedstawia przykład.
public static void SimpleExtraction()
{
JsonSerializerOptions options = JsonSerializerOptions.Default;
JsonNode schema = options.GetJsonSchemaAsNode(typeof(Person));
Console.WriteLine(schema.ToString());
//{
// "type": ["object", "null"],
// "properties": {
// "Name": { "type": "string" },
// "Age": { "type": "integer" },
// "Address": { "type": ["string", "null"], "default": null }
// },
// "required": ["Name", "Age"]
//}
}
record Person(string Name, int Age, string? Address = null);
Jak widać w tym przykładzie, eksportujący rozróżnia właściwości dopuszczające wartość null i niedopuszczające wartość null, a ustawia słowo kluczowe required na podstawie tego, czy parametr funkcji konstruktora jest opcjonalny, czy nie.
Począwszy od wersji .NET 11 eksporter rozpoznaje typy BFloat16, Decimal32, Decimal64 i Decimal128. Eksportuje schematy dla typów dopuszczających wartość null oraz dla nazwanych literałów po włączeniu AllowNamedFloatingPointLiterals.
Schematy dla typów unii
Począwszy od .NET 11, JsonSchemaExporter opisuje unię języka C# z nieoznakowanym anyOf schematem, który ma gałąź dla każdego przypadku. Unia nie dodaje dyskryminatora, ponieważ JsonSerializer zapisuje tylko aktywny wariant. Natomiast gdy włączysz wnioskowanie dla zamkniętej hierarchii, kod JSON serializowany jako typ podstawowy tej zamkniętej hierarchii zawiera deskryminator $type, który identyfikuje typ pochodny. Opisane tutaj anyOf to dane wyjściowe JsonSchemaExporter; platforma ASP.NET Core generuje dokumenty OpenAPI oddzielnie.
Konfigurowanie danych wyjściowych schematu
Możesz wpływać na wynik schematu poprzez konfigurację określoną w wystąpieniu JsonSerializerOptions lub JsonTypeInfo, w którym wywoływana jest metoda GetJsonSchemaAsNode. Poniższy przykład ustawia zasady nazewnictwa na KebabCaseUpper, zapisuje liczby jako ciągi i nie zezwala na niezamapowane właściwości.
public static void CustomExtraction()
{
JsonSerializerOptions options = new(JsonSerializerOptions.Default)
{
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseUpper,
NumberHandling = JsonNumberHandling.WriteAsString,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
};
JsonNode schema = options.GetJsonSchemaAsNode(typeof(MyPoco));
Console.WriteLine(schema.ToString());
//{
// "type": ["object", "null"],
// "properties": {
// "NUMERIC-VALUE": {
// "type": ["string", "integer"],
// "pattern": "^-?(?:0|[1-9]\\d*)$"
// }
// },
// "additionalProperties": false
//}
}
class MyPoco
{
public int NumericValue { get; init; }
}
Możesz dodatkowo kontrolować wygenerowany schemat przy użyciu JsonSchemaExporterOptions typu konfiguracji. W poniższym przykładzie właściwość TreatNullObliviousAsNonNullable jest ustawiana na true w celu oznaczenia typów na poziomie głównym jako niemogące być null.
public static void CustomExtraction()
{
JsonSerializerOptions options = JsonSerializerOptions.Default;
JsonSchemaExporterOptions exporterOptions = new()
{
TreatNullObliviousAsNonNullable = true,
};
JsonNode schema = options.GetJsonSchemaAsNode(typeof(Person), exporterOptions);
Console.WriteLine(schema.ToString());
//{
// "type": "object",
// "properties": {
// "Name": { "type": "string" }
// },
// "required": ["Name"]
//}
}
record Person(string Name);
Przekształcanie wygenerowanego schematu
Możesz zastosować własne przekształcenia do wygenerowanych węzłów schematu, określając delegata TransformSchemaNode . Poniższy przykład zawiera tekst z adnotacji DescriptionAttribute w wygenerowanym schemacie.
JsonSchemaExporterOptions exporterOptions = new()
{
TransformSchemaNode = (context, schema) =>
{
// Determine if a type or property and extract the relevant attribute provider.
ICustomAttributeProvider? attributeProvider = context.PropertyInfo is not null
? context.PropertyInfo.AttributeProvider
: context.TypeInfo.Type;
// Look up any description attributes.
DescriptionAttribute? descriptionAttr = attributeProvider?
.GetCustomAttributes(inherit: true)
.Select(attr => attr as DescriptionAttribute)
.FirstOrDefault(attr => attr is not null);
// Apply description attribute to the generated schema.
if (descriptionAttr != null)
{
if (schema is not JsonObject jObj)
{
// Handle the case where the schema is a Boolean.
JsonValueKind valueKind = schema.GetValueKind();
Debug.Assert(valueKind is JsonValueKind.True or JsonValueKind.False);
schema = jObj = new JsonObject();
if (valueKind is JsonValueKind.False)
{
jObj.Add("not", true);
}
}
jObj.Insert(0, "description", descriptionAttr.Description);
}
return schema;
}
};
Poniższy przykład kodu generuje schemat zawierający description słowo-kluczowe pochodzące z DescriptionAttribute adnotacji.
JsonSerializerOptions options = JsonSerializerOptions.Default;
JsonNode schema = options.GetJsonSchemaAsNode(typeof(Person), exporterOptions);
Console.WriteLine(schema.ToString());
//{
// "description": "A person",
// "type": ["object", "null"],
// "properties": {
// "Name": { "description": "The name of the person", "type": "string" }
// },
// "required": ["Name"]
//}
[Description("A person")]
record Person([property: Description("The name of the person")] string Name);