diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchema.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchema.cs
index d261b374b390a..0948acc19bb92 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchema.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchema.cs
@@ -34,6 +34,14 @@ public JsonSchema() { }
public bool IsTrue => _trueOrFalse is true;
public bool IsFalse => _trueOrFalse is false;
+
+ ///
+ /// Per the JSON schema core specification section 4.3
+ /// (https://json-schema.org/draft/2020-12/json-schema-core#name-json-schema-documents)
+ /// A JSON schema must either be an object or a boolean.
+ /// We represent false and true schemas using this flag.
+ /// It is not possible to specify keywords in boolean schemas.
+ ///
private readonly bool? _trueOrFalse;
public string? Ref { get => _ref; set { VerifyMutable(); _ref = value; } }
@@ -95,6 +103,7 @@ public int KeywordCount
{
if (_trueOrFalse != null)
{
+ // Boolean schemas admit no keywords
return 0;
}
@@ -129,6 +138,7 @@ public void MakeNullable()
{
if (_trueOrFalse != null)
{
+ // boolean schemas do not admit type keywords.
return;
}
@@ -260,6 +270,23 @@ JsonNode CompleteSchema(JsonNode schema)
}
}
+ ///
+ /// If the schema is boolean, replaces it with a semantically
+ /// equivalent object schema that allows appending keywords.
+ ///
+ public static void EnsureMutable(ref JsonSchema schema)
+ {
+ switch (schema._trueOrFalse)
+ {
+ case false:
+ schema = new JsonSchema { Not = True };
+ break;
+ case true:
+ schema = new JsonSchema();
+ break;
+ }
+ }
+
private static ReadOnlySpan s_schemaValues =>
[
// NB the order of these values influences order of types in the rendered schema
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs
index 063bd95147673..9690b2b308def 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Schema/JsonSchemaExporter.cs
@@ -240,6 +240,7 @@ private static JsonSchema MapJsonSchemaCore(
if (property.AssociatedParameter is { HasDefaultValue: true } parameterInfo)
{
+ JsonSchema.EnsureMutable(ref propertySchema);
propertySchema.DefaultValue = JsonSerializer.SerializeToNode(parameterInfo.DefaultValue, property.JsonTypeInfo);
propertySchema.HasDefaultValue = true;
}
diff --git a/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.TestTypes.cs b/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.TestTypes.cs
index 12ee630b378fb..0f247cab034bc 100644
--- a/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.TestTypes.cs
+++ b/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.TestTypes.cs
@@ -1034,6 +1034,18 @@ public static IEnumerable GetTestDataCore()
}
""");
+ yield return new TestData(
+ Value: new(value: null),
+ AdditionalValues: [new(true), new(42), new(""), new(new object()), new(Array.Empty())],
+ ExpectedJsonSchema: """
+ {
+ "type": ["object","null"],
+ "properties": {
+ "Value": { "default": null }
+ }
+ }
+ """);
+
// Collection types
yield return new TestData([1, 2, 3], ExpectedJsonSchema: """{"type":["array","null"],"items":{"type":"integer"}}""");
yield return new TestData>([false, true, false], ExpectedJsonSchema: """{"type":["array","null"],"items":{"type":"boolean"}}""");
@@ -1441,6 +1453,11 @@ public class ClassWithJsonPointerEscapablePropertyNames
public PocoWithRecursiveMembers Value { get; set; }
}
+ public class ClassWithOptionalObjectParameter(object? value = null)
+ {
+ public object? Value { get; } = value;
+ }
+
public readonly struct StructDictionary(IEnumerable> values)
: IReadOnlyDictionary
where TKey : notnull
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSchemaExporterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSchemaExporterTests.cs
index a7b0775361de9..6946ea661b561 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSchemaExporterTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSchemaExporterTests.cs
@@ -107,6 +107,7 @@ public sealed partial class JsonSchemaExporterTests_SourceGen()
[JsonSerializable(typeof(PocoCombiningPolymorphicTypeAndDerivedTypes))]
[JsonSerializable(typeof(ClassWithComponentModelAttributes))]
[JsonSerializable(typeof(ClassWithJsonPointerEscapablePropertyNames))]
+ [JsonSerializable(typeof(ClassWithOptionalObjectParameter))]
// Collection types
[JsonSerializable(typeof(int[]))]
[JsonSerializable(typeof(List))]