Exportateur de schéma JSON
La classe JsonSchemaExporter, introduite dans .NET 9, vous permet d'extraire des documents de schéma JSON à partir de types .NET en utilisant soit une instance de JsonSerializerOptions soit de JsonTypeInfo. Le schéma résultant fournit une spécification du contrat de sérialisation JSON pour le type .NET. Le schéma décrit la structure de ce qui serait sérialisé et de ce qui peut être désérialisé.
L’extrait de code suivant affiche un exemple.
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);
Comme on peut le voir dans cet exemple, l'exportateur fait la distinction entre les propriétés nullables et non nullables, et il remplit le mot-clé required
en vertu du fait qu'un paramètre de constructeur est facultatif ou non.
Configurez la sortie du schéma
Vous pouvez influencer la sortie du schéma par la configuration spécifiée dans l'instance JsonSerializerOptions ou JsonTypeInfo sur laquelle vous appelez la méthode GetJsonSchemaAsNode. L'exemple suivant définit la stratégie de dénomination à KebabCaseUpper, écrit les nombres sous forme de chaînes et interdit les propriétés non mappées.
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; }
}
Vous pouvez contrôler davantage le schéma généré en utilisant le type de configuration JsonSchemaExporterOptions. L'exemple suivant définit la propriété TreatNullObliviousAsNonNullable à true
pour marquer les types de niveau racine comme non annulables.
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);
Transformez le schéma généré
Vous pouvez appliquer vos propres transformations aux nœuds de schéma générés en spécifiant un délégué TransformSchemaNode. L'exemple suivant incorpore le texte des annotations DescriptionAttribute dans le schéma généré.
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;
}
};
L'exemple de code suivant génère un schéma qui incorpore un mot-clé source description
à partir d'annotations DescriptionAttribute :
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);