Skip to content

Commit 6ec5931

Browse files
Add API entry for JsonDocument converter (#60236)
* Add API entry for JsonDocument converter * Add reference to System.Text.Json * Add JsonElementConverter from MetadataServices * Correct converter naming * Add JsonDocument type to source generation * include testing Co-authored-by: Eirik Tsarpalis <[email protected]>
1 parent d5ce000 commit 6ec5931

11 files changed

+91
-7
lines changed

src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ private sealed class Parser
3434
private const string JsonNodeFullName = "System.Text.Json.Nodes.JsonNode";
3535
private const string JsonObjectFullName = "System.Text.Json.Nodes.JsonObject";
3636
private const string JsonValueFullName = "System.Text.Json.Nodes.JsonValue";
37+
private const string JsonDocumentFullName = "System.Text.Json.JsonDocument";
3738
private const string JsonIgnoreAttributeFullName = "System.Text.Json.Serialization.JsonIgnoreAttribute";
3839
private const string JsonIgnoreConditionFullName = "System.Text.Json.Serialization.JsonIgnoreCondition";
3940
private const string JsonIncludeAttributeFullName = "System.Text.Json.Serialization.JsonIncludeAttribute";
@@ -92,6 +93,7 @@ private sealed class Parser
9293
private readonly Type? _jsonNodeType;
9394
private readonly Type? _jsonObjectType;
9495
private readonly Type? _jsonValueType;
96+
private readonly Type? _jsonDocumentType;
9597

9698
// Unsupported types
9799
private readonly Type _delegateType;
@@ -229,6 +231,7 @@ public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGene
229231
_jsonNodeType = _metadataLoadContext.Resolve(JsonNodeFullName);
230232
_jsonObjectType = _metadataLoadContext.Resolve(JsonObjectFullName);
231233
_jsonValueType = _metadataLoadContext.Resolve(JsonValueFullName);
234+
_jsonDocumentType = _metadataLoadContext.Resolve(JsonDocumentFullName);
232235

233236
// Unsupported types.
234237
_delegateType = _metadataLoadContext.Resolve(SpecialType.System_Delegate);
@@ -1556,6 +1559,7 @@ private void PopulateKnownTypes()
15561559
AddTypeIfNotNull(_knownTypes, _jsonNodeType);
15571560
AddTypeIfNotNull(_knownTypes, _jsonObjectType);
15581561
AddTypeIfNotNull(_knownTypes, _jsonValueType);
1562+
AddTypeIfNotNull(_knownTypes, _jsonDocumentType);
15591563

15601564
_knownUnsupportedTypes.Add(_typeType);
15611565
_knownUnsupportedTypes.Add(_serializationInfoType);

src/libraries/System.Text.Json/ref/System.Text.Json.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ public static partial class JsonMetadataServices
10261026
public static System.Text.Json.Serialization.JsonConverter<System.Text.Json.Nodes.JsonNode> JsonNodeConverter { get { throw null; } }
10271027
public static System.Text.Json.Serialization.JsonConverter<System.Text.Json.Nodes.JsonObject> JsonObjectConverter { get { throw null; } }
10281028
public static System.Text.Json.Serialization.JsonConverter<System.Text.Json.Nodes.JsonValue> JsonValueConverter { get { throw null; } }
1029+
public static System.Text.Json.Serialization.JsonConverter<System.Text.Json.JsonDocument> JsonDocumentConverter { get { throw null; } }
10291030
public static System.Text.Json.Serialization.JsonConverter<object> ObjectConverter { get { throw null; } }
10301031
[System.CLSCompliantAttribute(false)]
10311032
public static System.Text.Json.Serialization.JsonConverter<sbyte> SByteConverter { get { throw null; } }

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ private static Dictionary<Type, JsonConverter> GetDefaultSimpleConverters()
111111
Add(JsonMetadataServices.Int16Converter);
112112
Add(JsonMetadataServices.Int32Converter);
113113
Add(JsonMetadataServices.Int64Converter);
114-
Add(new JsonElementConverter());
115-
Add(new JsonDocumentConverter());
114+
Add(JsonMetadataServices.JsonElementConverter);
115+
Add(JsonMetadataServices.JsonDocumentConverter);
116116
Add(JsonMetadataServices.ObjectConverter);
117117
Add(JsonMetadataServices.SByteConverter);
118118
Add(JsonMetadataServices.SingleConverter);

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Converters.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Collections.Generic;
54
using System.Text.Json.Nodes;
65
using System.Text.Json.Serialization.Converters;
76

@@ -128,6 +127,13 @@ public static partial class JsonMetadataServices
128127
public static JsonConverter<JsonValue> JsonValueConverter => s_jsonValueConverter ??= new JsonValueConverter();
129128
private static JsonConverter<JsonValue>? s_jsonValueConverter;
130129

130+
/// <summary>
131+
/// Returns a <see cref="JsonConverter{T}"/> instance that converts <see cref="JsonDocument"/> values.
132+
/// </summary>
133+
/// <remarks>This API is for use by the output of the System.Text.Json source generator and should not be called directly.</remarks>
134+
public static JsonConverter<JsonDocument> JsonDocumentConverter => s_jsonDocumentConverter ??= new JsonDocumentConverter();
135+
private static JsonConverter<JsonDocument>? s_jsonDocumentConverter;
136+
131137
/// <summary>
132138
/// Returns a <see cref="JsonConverter{T}"/> instance that converts <see cref="object"/> values.
133139
/// </summary>

src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs

+9-4
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ public static void AssertJsonEqual(string expected, string actual)
1818
{
1919
using JsonDocument expectedDom = JsonDocument.Parse(expected);
2020
using JsonDocument actualDom = JsonDocument.Parse(actual);
21-
AssertJsonEqual(expectedDom.RootElement, actualDom.RootElement, new());
21+
AssertJsonEqual(expectedDom.RootElement, actualDom.RootElement);
2222
}
2323

24-
private static void AssertJsonEqual(JsonElement expected, JsonElement actual, Stack<object> path)
24+
public static void AssertJsonEqual(JsonElement expected, JsonElement actual)
25+
{
26+
AssertJsonEqualCore(expected, actual, new());
27+
}
28+
29+
private static void AssertJsonEqualCore(JsonElement expected, JsonElement actual, Stack<object> path)
2530
{
2631
JsonValueKind valueKind = expected.ValueKind;
2732
AssertTrue(passCondition: valueKind == actual.ValueKind);
@@ -54,7 +59,7 @@ private static void AssertJsonEqual(JsonElement expected, JsonElement actual, St
5459
foreach (string name in expectedProperties)
5560
{
5661
path.Push(name);
57-
AssertJsonEqual(expected.GetProperty(name), actual.GetProperty(name), path);
62+
AssertJsonEqualCore(expected.GetProperty(name), actual.GetProperty(name), path);
5863
path.Pop();
5964
}
6065
break;
@@ -67,7 +72,7 @@ private static void AssertJsonEqual(JsonElement expected, JsonElement actual, St
6772
{
6873
AssertTrue(passCondition: actualEnumerator.MoveNext(), "Actual array contains fewer elements.");
6974
path.Push(i++);
70-
AssertJsonEqual(expectedEnumerator.Current, actualEnumerator.Current, path);
75+
AssertJsonEqualCore(expectedEnumerator.Current, actualEnumerator.Current, path);
7176
path.Pop();
7277
}
7378

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public interface ITestContext
3434
public JsonTypeInfo<byte[]> ByteArray { get; }
3535
public JsonTypeInfo<string> String { get; }
3636
public JsonTypeInfo<(string Label1, int Label2, bool)> ValueTupleStringInt32Boolean { get; }
37+
public JsonTypeInfo<JsonDocument> JsonDocument { get; }
38+
public JsonTypeInfo<JsonElement> JsonElement { get; }
3739
public JsonTypeInfo<RealWorldContextTests.ClassWithEnumAndNullable> ClassWithEnumAndNullable { get; }
3840
public JsonTypeInfo<RealWorldContextTests.ClassWithNullableProperties> ClassWithNullableProperties { get; }
3941
public JsonTypeInfo<ClassWithCustomConverter> ClassWithCustomConverter { get; }

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ namespace System.Text.Json.SourceGeneration.Tests
3030
[JsonSerializable(typeof(object[]))]
3131
[JsonSerializable(typeof(string))]
3232
[JsonSerializable(typeof((string Label1, int Label2, bool)))]
33+
[JsonSerializable(typeof(JsonDocument))]
34+
[JsonSerializable(typeof(JsonElement))]
3335
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
3436
[JsonSerializable(typeof(RealWorldContextTests.ClassWithNullableProperties))]
3537
[JsonSerializable(typeof(ClassWithCustomConverter))]
@@ -81,6 +83,8 @@ public override void EnsureFastPathGeneratedAsExpected()
8183
Assert.Null(MetadataAndSerializationContext.Default.SampleEnum.SerializeHandler);
8284
Assert.Null(MetadataAndSerializationContext.Default.String.SerializeHandler);
8385
Assert.NotNull(MetadataAndSerializationContext.Default.ValueTupleStringInt32Boolean.SerializeHandler);
86+
Assert.Null(MetadataAndSerializationContext.Default.JsonDocument.SerializeHandler);
87+
Assert.Null(MetadataAndSerializationContext.Default.JsonElement.SerializeHandler);
8488
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithEnumAndNullable.SerializeHandler);
8589
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithNullableProperties.SerializeHandler);
8690
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverter);
@@ -97,6 +101,7 @@ public override void EnsureFastPathGeneratedAsExpected()
97101
Assert.NotNull(MetadataAndSerializationContext.Default.PersonStruct.SerializeHandler);
98102
Assert.NotNull(MetadataAndSerializationContext.Default.TypeWithValidationAttributes.SerializeHandler);
99103
Assert.NotNull(MetadataAndSerializationContext.Default.TypeWithDerivedAttribute.SerializeHandler);
104+
Assert.Null(MetadataAndSerializationContext.Default.PolymorphicClass.SerializeHandler);
100105
}
101106
}
102107
}

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ namespace System.Text.Json.SourceGeneration.Tests
2929
[JsonSerializable(typeof(byte[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
3030
[JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata)]
3131
[JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Metadata)]
32+
[JsonSerializable(typeof(JsonDocument), GenerationMode = JsonSourceGenerationMode.Metadata)]
33+
[JsonSerializable(typeof(JsonElement), GenerationMode = JsonSourceGenerationMode.Metadata)]
3234
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata)]
3335
[JsonSerializable(typeof(RealWorldContextTests.ClassWithNullableProperties), GenerationMode = JsonSourceGenerationMode.Metadata)]
3436
[JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
@@ -79,6 +81,8 @@ public override void EnsureFastPathGeneratedAsExpected()
7981
Assert.Null(MetadataWithPerTypeAttributeContext.Default.SampleEnum.SerializeHandler);
8082
Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.SerializeHandler);
8183
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.SerializeHandler);
84+
Assert.Null(MetadataWithPerTypeAttributeContext.Default.JsonDocument.SerializeHandler);
85+
Assert.Null(MetadataWithPerTypeAttributeContext.Default.JsonElement.SerializeHandler);
8286
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.SerializeHandler);
8387
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithNullableProperties.SerializeHandler);
8488
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverter.SerializeHandler);
@@ -95,6 +99,7 @@ public override void EnsureFastPathGeneratedAsExpected()
9599
Assert.Null(MetadataWithPerTypeAttributeContext.Default.PersonStruct.SerializeHandler);
96100
Assert.Null(MetadataWithPerTypeAttributeContext.Default.TypeWithValidationAttributes.SerializeHandler);
97101
Assert.Null(MetadataWithPerTypeAttributeContext.Default.TypeWithDerivedAttribute.SerializeHandler);
102+
Assert.Null(MetadataWithPerTypeAttributeContext.Default.PolymorphicClass.SerializeHandler);
98103
}
99104
}
100105

@@ -121,6 +126,8 @@ public override void EnsureFastPathGeneratedAsExpected()
121126
[JsonSerializable(typeof(byte[]))]
122127
[JsonSerializable(typeof(string))]
123128
[JsonSerializable(typeof((string Label1, int Label2, bool)))]
129+
[JsonSerializable(typeof(JsonDocument))]
130+
[JsonSerializable(typeof(JsonElement))]
124131
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
125132
[JsonSerializable(typeof(RealWorldContextTests.ClassWithNullableProperties))]
126133
[JsonSerializable(typeof(ClassWithCustomConverter))]
@@ -194,6 +201,8 @@ public override void EnsureFastPathGeneratedAsExpected()
194201
Assert.Null(MetadataContext.Default.SampleEnum.SerializeHandler);
195202
Assert.Null(MetadataContext.Default.String.SerializeHandler);
196203
Assert.Null(MetadataContext.Default.ValueTupleStringInt32Boolean.SerializeHandler);
204+
Assert.Null(MetadataContext.Default.JsonDocument.SerializeHandler);
205+
Assert.Null(MetadataContext.Default.JsonElement.SerializeHandler);
197206
Assert.Null(MetadataContext.Default.ClassWithEnumAndNullable.SerializeHandler);
198207
Assert.Null(MetadataContext.Default.ClassWithNullableProperties.SerializeHandler);
199208
Assert.Null(MetadataContext.Default.ClassWithCustomConverter.SerializeHandler);
@@ -210,6 +219,7 @@ public override void EnsureFastPathGeneratedAsExpected()
210219
Assert.Null(MetadataContext.Default.PersonStruct.SerializeHandler);
211220
Assert.Null(MetadataContext.Default.TypeWithValidationAttributes.SerializeHandler);
212221
Assert.Null(MetadataContext.Default.TypeWithDerivedAttribute.SerializeHandler);
222+
Assert.Null(MetadataContext.Default.PolymorphicClass.SerializeHandler);
213223
}
214224

215225
[Fact]

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ namespace System.Text.Json.SourceGeneration.Tests
3030
[JsonSerializable(typeof(byte[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
3131
[JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
3232
[JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
33+
[JsonSerializable(typeof(JsonDocument), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
34+
[JsonSerializable(typeof(JsonElement), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
3335
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
3436
[JsonSerializable(typeof(RealWorldContextTests.ClassWithNullableProperties), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
3537
[JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
@@ -81,6 +83,8 @@ public override void EnsureFastPathGeneratedAsExpected()
8183
Assert.Null(MixedModeContext.Default.SampleEnum.SerializeHandler);
8284
Assert.Null(MixedModeContext.Default.String.SerializeHandler);
8385
Assert.NotNull(MixedModeContext.Default.ValueTupleStringInt32Boolean.SerializeHandler);
86+
Assert.Null(MixedModeContext.Default.JsonDocument.SerializeHandler);
87+
Assert.Null(MixedModeContext.Default.JsonElement.SerializeHandler);
8488
Assert.NotNull(MixedModeContext.Default.ClassWithEnumAndNullable.SerializeHandler);
8589
Assert.NotNull(MixedModeContext.Default.ClassWithNullableProperties.SerializeHandler);
8690
Assert.Null(MixedModeContext.Default.ClassWithCustomConverter.SerializeHandler);
@@ -97,6 +101,7 @@ public override void EnsureFastPathGeneratedAsExpected()
97101
Assert.NotNull(MixedModeContext.Default.PersonStruct.SerializeHandler);
98102
Assert.NotNull(MixedModeContext.Default.TypeWithValidationAttributes.SerializeHandler);
99103
Assert.NotNull(MixedModeContext.Default.TypeWithDerivedAttribute.SerializeHandler);
104+
Assert.Null(MixedModeContext.Default.PolymorphicClass.SerializeHandler);
100105
}
101106

102107
[Fact]

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs

+34
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,40 @@ public virtual void RoundTripTypeNameClash()
111111
VerifyRepeatedLocation(expected, obj);
112112
}
113113

114+
[Theory]
115+
[InlineData("0")]
116+
[InlineData("false")]
117+
[InlineData("\"str\"")]
118+
[InlineData("[1,2,3]")]
119+
[InlineData("{ \"key\" : \"value\" }")]
120+
public void RoundtripJsonDocument(string json)
121+
{
122+
JsonDocument jsonDocument = JsonDocument.Parse(json);
123+
124+
string actualJson = JsonSerializer.Serialize(jsonDocument, DefaultContext.JsonDocument);
125+
JsonTestHelper.AssertJsonEqual(json, actualJson);
126+
127+
JsonDocument actualJsonDocument = JsonSerializer.Deserialize(actualJson, DefaultContext.JsonDocument);
128+
JsonTestHelper.AssertJsonEqual(jsonDocument.RootElement, actualJsonDocument.RootElement);
129+
}
130+
131+
[Theory]
132+
[InlineData("0")]
133+
[InlineData("false")]
134+
[InlineData("\"str\"")]
135+
[InlineData("[1,2,3]")]
136+
[InlineData("{ \"key\" : \"value\" }")]
137+
public void RoundtripJsonElement(string json)
138+
{
139+
JsonElement jsonElement = JsonDocument.Parse(json).RootElement;
140+
141+
string actualJson = JsonSerializer.Serialize(jsonElement, DefaultContext.JsonElement);
142+
JsonTestHelper.AssertJsonEqual(json, actualJson);
143+
144+
JsonElement actualJsonElement = JsonSerializer.Deserialize(actualJson, DefaultContext.JsonElement);
145+
JsonTestHelper.AssertJsonEqual(jsonElement, actualJsonElement);
146+
}
147+
114148
[Fact]
115149
public virtual void RoundTripValueTuple()
116150
{

0 commit comments

Comments
 (0)