다음을 통해 공유


JSON 스키마 내보내기

JsonSchemaExporter.NET 9에 도입된 클래스를 사용하면 A 또는 인스턴스를 사용하여 JsonSerializerOptions .NET 형식에서 JSON 스키마JsonTypeInfo할 수 있습니다. 결과 스키마는 .NET 형식에 대한 JSON serialization 계약의 사양을 제공합니다. 스키마는 serialize할 개체와 역직렬화할 수 있는 요소의 모양을 설명합니다.

다음 코드 조각은 예제를 보여 줍니다.

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);

이 예제에서 볼 수 있듯이 내보내기는 null을 허용하는 속성과 null을 허용하지 않는 속성을 구분하며 생성자 매개 변수가 선택 사항인지 여부에 따라 required 키워드를 채웁니다.

스키마 출력 구성

JsonSerializerOptions 메서드를 호출하는 JsonTypeInfo 또는 GetJsonSchemaAsNode 인스턴스에 지정된 구성에 따라 스키마 출력에 영향을 줄 수 있습니다. 다음은 명명 정책을 KebabCaseUpper로 설정하고, 숫자를 문자열로 쓰고, 매핑되지 않은 속성을 허용하지 않는 예제입니다.

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; }
}

JsonSchemaExporterOptions 구성 유형을 사용하여 생성된 스키마를 추가로 제어할 수 있습니다. 다음은 TreatNullObliviousAsNonNullable 속성을 true로 설정하여 루트 수준 형식을 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);

생성된 스키마 변환

TransformSchemaNode 대리자를 지정하여 생성된 스키마 노드에 고유한 변환을 적용할 수 있습니다. 다음 예제에서는 DescriptionAttribute 주석의 텍스트를 생성된 스키마에 통합합니다.

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;
    }
};

다음 코드 예제에서는 description 주석에서 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);