string getterValue = property switch
{
{ DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "null",
- { CanUseGetter: true } => $"static (obj) => (({declaringTypeFQN})obj).{propertyName}",
+ { CanUseGetter: true } => $"static obj => (({declaringTypeFQN})obj).{propertyName}",
{ CanUseGetter: false, HasJsonInclude: true }
- => $"""static (obj) => throw new {InvalidOperationExceptionTypeRef}("{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.TypeRef.Name, propertyName)}")""",
+ => $"""static _ => throw new {InvalidOperationExceptionTypeRef}("{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.TypeRef.Name, propertyName)}")""",
_ => "null"
};
const string ArgsVarName = "args";
- StringBuilder sb = new($"static ({ArgsVarName}) => new {typeGenerationSpec.TypeRef.FullyQualifiedName}(");
+ StringBuilder sb = new($"static {ArgsVarName} => new {typeGenerationSpec.TypeRef.FullyQualifiedName}(");
if (parameters.Count > 0)
{
if (state.IsPropertyOrderSpecified)
{
- typeInfo.SortProperties();
+ typeInfo.PropertyList.SortProperties();
}
}
}
Debug.Assert(jsonPropertyInfo.Name != null);
- typeInfo.AddProperty(jsonPropertyInfo, ref state);
+ typeInfo.PropertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state);
}
[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
/// </summary>
private static JsonTypeInfo<T> CreateCore<T>(JsonConverter converter, JsonSerializerOptions options)
{
- JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options);
+ var typeInfo = new JsonTypeInfo<T>(converter, options);
typeInfo.PopulatePolymorphismMetadata();
typeInfo.MapInterfaceTypesToCallbacks();
private static JsonTypeInfo<T> CreateCore<T>(JsonSerializerOptions options, JsonObjectInfoValues<T> objectInfo)
{
JsonConverter<T> converter = GetConverter(objectInfo);
- JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options);
+ var typeInfo = new JsonTypeInfo<T>(converter, options);
if (objectInfo.ObjectWithParameterizedConstructorCreator != null)
{
typeInfo.CreateObjectWithArgs = objectInfo.ObjectWithParameterizedConstructorCreator;
typeInfo.CreateObjectForExtensionDataProperty = ((JsonTypeInfo)typeInfo).CreateObject;
}
- PopulateProperties(typeInfo, objectInfo.PropertyMetadataInitializer);
+ if (objectInfo.PropertyMetadataInitializer != null)
+ {
+ typeInfo.SourceGenDelayedPropertyInitializer = objectInfo.PropertyMetadataInitializer;
+ }
+ else
+ {
+ typeInfo.PropertyMetadataSerializationNotSupported = true;
+ }
+
typeInfo.SerializeHandler = objectInfo.SerializeHandler;
typeInfo.NumberHandling = objectInfo.NumberHandling;
typeInfo.PopulatePolymorphismMetadata();
object? createObjectWithArgs = null,
object? addFunc = null)
{
+ if (collectionInfo is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(nameof(collectionInfo));
+ }
+
converter = collectionInfo.SerializeHandler != null
? new JsonMetadataServicesConverter<T>(converter)
: converter;
JsonTypeInfo<T> typeInfo = new JsonTypeInfo<T>(converter, options);
- if (collectionInfo is null)
- {
- ThrowHelper.ThrowArgumentNullException(nameof(collectionInfo));
- }
typeInfo.KeyTypeInfo = collectionInfo.KeyInfo;
typeInfo.ElementTypeInfo = collectionInfo.ElementInfo;
}
}
- private static void PopulateProperties(JsonTypeInfo typeInfo, Func<JsonSerializerContext, JsonPropertyInfo[]?>? propInitFunc)
+ internal static void PopulateProperties(JsonTypeInfo typeInfo, JsonTypeInfo.JsonPropertyInfoList propertyList, Func<JsonSerializerContext, JsonPropertyInfo[]> propInitFunc)
{
Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object);
- Debug.Assert(!typeInfo.IsReadOnly);
-
- JsonSerializerContext? context = (typeInfo.OriginatingResolver ?? typeInfo.Options.TypeInfoResolver) as JsonSerializerContext;
- if (propInitFunc?.Invoke(context!) is not JsonPropertyInfo[] properties)
- {
- if (typeInfo.Type == JsonTypeInfo.ObjectType)
- {
- return;
- }
+ Debug.Assert(!typeInfo.IsConfigured);
+ Debug.Assert(typeInfo.Type != JsonTypeInfo.ObjectType);
+ Debug.Assert(typeInfo.Converter.ElementType is null);
- if (typeInfo.Converter.ElementType != null)
- {
- // Nullable<> or F# optional converter strategy is set to element strategy
- return;
- }
-
- typeInfo.PropertyMetadataSerializationNotSupported = true;
- return;
- }
+ JsonSerializerContext? context = typeInfo.Options.TypeInfoResolver as JsonSerializerContext;
+ JsonPropertyInfo[] properties = propInitFunc(context!);
// TODO update the source generator so that all property
// hierarchy resolution is happening at compile time.
continue;
}
- typeInfo.AddProperty(jsonPropertyInfo, ref state);
+ propertyList.AddPropertyWithConflictResolution(jsonPropertyInfo, ref state);
}
// NB we don't need to sort source gen properties here since they were already sorted at compile time.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
-using System.Text.Json.Reflection;
namespace System.Text.Json.Serialization.Metadata
{
/// It is required that added <see cref="JsonPropertyInfo"/> entries are unique up to <see cref="JsonPropertyInfo.Name"/>,
/// however this will only be validated on serialization, once the metadata instance gets locked for further modification.
/// </remarks>
- public IList<JsonPropertyInfo> Properties => _properties ??= new(this);
- private JsonPropertyInfoList? _properties;
+ public IList<JsonPropertyInfo> Properties => PropertyList;
- internal void SortProperties()
+ internal JsonPropertyInfoList PropertyList
{
- Debug.Assert(!IsConfigured);
- Debug.Assert(_properties != null && _properties.Count > 0);
+ get
+ {
+ return _properties ?? CreatePropertyList();
+ JsonPropertyInfoList CreatePropertyList()
+ {
+ var list = new JsonPropertyInfoList(this);
+ if (_sourceGenDelayedPropertyInitializer is { } propInit)
+ {
+ // .NET 6 source gen backward compatibility -- ensure that the
+ // property initializer delegate is invoked lazily.
+ JsonMetadataServices.PopulateProperties(this, list, propInit);
+ }
- _properties.SortProperties();
- PropertyCache?.List.StableSortByKey(static propInfo => propInfo.Value.Order);
+ JsonPropertyInfoList? result = Interlocked.CompareExchange(ref _properties, list, null);
+ _sourceGenDelayedPropertyInitializer = null;
+ return result ?? list;
+ }
+ }
}
/// <summary>
+ /// Stores the .NET 6-style property initialization delegate for delayed evaluation.
+ /// </summary>
+ internal Func<JsonSerializerContext, JsonPropertyInfo[]>? SourceGenDelayedPropertyInitializer
+ {
+ get => _sourceGenDelayedPropertyInitializer;
+ set
+ {
+ Debug.Assert(!IsReadOnly);
+ Debug.Assert(_properties is null, "must not be set if a property list has been initialized.");
+ _sourceGenDelayedPropertyInitializer = value;
+ }
+ }
+
+ private Func<JsonSerializerContext, JsonPropertyInfo[]>? _sourceGenDelayedPropertyInitializer;
+ private JsonPropertyInfoList? _properties;
+
+ /// <summary>
/// Gets or sets a configuration object specifying polymorphism metadata.
/// </summary>
/// <exception cref="ArgumentException">
internal abstract ValueTask<object?> DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken);
internal abstract object? DeserializeAsObject(Stream utf8Json);
- /// <summary>
- /// Used by the built-in resolvers to add property metadata applying conflict resolution.
- /// </summary>
- internal void AddProperty(JsonPropertyInfo jsonPropertyInfo, ref PropertyHierarchyResolutionState state)
- {
- Debug.Assert(jsonPropertyInfo.MemberName != null, "MemberName can be null in custom JsonPropertyInfo instances and should never be passed in this method");
- string memberName = jsonPropertyInfo.MemberName;
-
- JsonPropertyInfoList properties = _properties ??= new(this);
-
- if (state.AddedProperties.TryAdd(jsonPropertyInfo.Name, (jsonPropertyInfo, properties.Count)))
- {
- properties.Add(jsonPropertyInfo);
- state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
- }
- else
- {
- // The JsonPropertyNameAttribute or naming policy resulted in a collision.
- (JsonPropertyInfo other, int index) = state.AddedProperties[jsonPropertyInfo.Name];
-
- if (other.IsIgnored)
- {
- // Overwrite previously cached property since it has [JsonIgnore].
- state.AddedProperties[jsonPropertyInfo.Name] = (jsonPropertyInfo, index);
- properties[index] = jsonPropertyInfo;
- state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
- }
- else
- {
- bool ignoreCurrentProperty;
-
- if (!Type.IsInterface)
- {
- ignoreCurrentProperty =
- // Does the current property have `JsonIgnoreAttribute`?
- jsonPropertyInfo.IsIgnored ||
- // Is the current property hidden by the previously cached property
- // (with `new` keyword, or by overriding)?
- other.MemberName == memberName ||
- // Was a property with the same CLR name ignored? That property hid the current property,
- // thus, if it was ignored, the current property should be ignored too.
- state.IgnoredProperties?.ContainsKey(memberName) == true;
- }
- else
- {
- // Unlike classes, interface hierarchies reject all naming conflicts for non-ignored properties.
- // Conflicts like this are possible in two cases:
- // 1. Diamond ambiguity in property names, or
- // 2. Linear interface hierarchies that use properties with DIMs.
- //
- // Diamond ambiguities are not supported. Assuming there is demand, we might consider
- // adding support for DIMs in the future, however that would require adding more APIs
- // for the case of source gen.
-
- ignoreCurrentProperty = jsonPropertyInfo.IsIgnored;
- }
-
- if (!ignoreCurrentProperty)
- {
- ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(Type, jsonPropertyInfo.Name);
- }
- }
- }
-
- if (jsonPropertyInfo.IsIgnored)
- {
- (state.IgnoredProperties ??= new()).Add(memberName, jsonPropertyInfo);
- }
- }
-
internal ref struct PropertyHierarchyResolutionState
{
public PropertyHierarchyResolutionState() { }
Debug.Assert(PropertyCache is null);
Debug.Assert(ExtensionDataProperty is null);
- IList<JsonPropertyInfo> properties = (IList<JsonPropertyInfo>?)_properties ?? Array.Empty<JsonPropertyInfo>();
-
+ JsonPropertyInfoList properties = PropertyList;
JsonPropertyDictionary<JsonPropertyInfo> propertyCache = CreatePropertyCache(capacity: properties.Count);
+
int numberOfRequiredProperties = 0;
bool arePropertiesSorted = true;
int previousPropertyOrder = int.MinValue;
property.Configure();
}
- NumberOfRequiredProperties = numberOfRequiredProperties;
- PropertyCache = propertyCache;
-
if (!arePropertiesSorted)
{
// Properties have been configured by the user and require sorting.
- SortProperties();
+ properties.SortProperties();
+ propertyCache.List.StableSortByKey(static propInfo => propInfo.Value.Order);
}
+ NumberOfRequiredProperties = numberOfRequiredProperties;
+ PropertyCache = propertyCache;
+
// Override global UnmappedMemberHandling configuration
// if type specifies an extension data property.
EffectiveUnmappedMemberHandling = UnmappedMemberHandling ??
ParameterCount = jsonParameters.Length;
ParameterCache = parameterCache;
+ ParameterInfoValues = null;
}
internal static void ValidateType(Type type)
}
}
- private sealed class JsonPropertyInfoList : ConfigurationList<JsonPropertyInfo>
+ internal sealed class JsonPropertyInfoList : ConfigurationList<JsonPropertyInfo>
{
private readonly JsonTypeInfo _jsonTypeInfo;
_jsonTypeInfo = jsonTypeInfo;
}
- public override bool IsReadOnly => _jsonTypeInfo.IsReadOnly || _jsonTypeInfo.Kind != JsonTypeInfoKind.Object;
+ public override bool IsReadOnly => _jsonTypeInfo._properties == this && _jsonTypeInfo.IsReadOnly || _jsonTypeInfo.Kind != JsonTypeInfoKind.Object;
protected override void OnCollectionModifying()
{
- _jsonTypeInfo.VerifyMutable();
+ if (_jsonTypeInfo._properties == this)
+ {
+ _jsonTypeInfo.VerifyMutable();
+ }
if (_jsonTypeInfo.Kind != JsonTypeInfoKind.Object)
{
}
public void SortProperties()
- => _list.StableSortByKey(static propInfo => propInfo.Order);
+ {
+ _list.StableSortByKey(static propInfo => propInfo.Order);
+ }
+
+ /// <summary>
+ /// Used by the built-in resolvers to add property metadata applying conflict resolution.
+ /// </summary>
+ public void AddPropertyWithConflictResolution(JsonPropertyInfo jsonPropertyInfo, ref PropertyHierarchyResolutionState state)
+ {
+ Debug.Assert(!_jsonTypeInfo.IsConfigured);
+ Debug.Assert(jsonPropertyInfo.MemberName != null, "MemberName can be null in custom JsonPropertyInfo instances and should never be passed in this method");
+ string memberName = jsonPropertyInfo.MemberName;
+
+ if (state.AddedProperties.TryAdd(jsonPropertyInfo.Name, (jsonPropertyInfo, Count)))
+ {
+ Add(jsonPropertyInfo);
+ state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
+ }
+ else
+ {
+ // The JsonPropertyNameAttribute or naming policy resulted in a collision.
+ (JsonPropertyInfo other, int index) = state.AddedProperties[jsonPropertyInfo.Name];
+
+ if (other.IsIgnored)
+ {
+ // Overwrite previously cached property since it has [JsonIgnore].
+ state.AddedProperties[jsonPropertyInfo.Name] = (jsonPropertyInfo, index);
+ this[index] = jsonPropertyInfo;
+ state.IsPropertyOrderSpecified |= jsonPropertyInfo.Order != 0;
+ }
+ else
+ {
+ bool ignoreCurrentProperty;
+
+ if (!_jsonTypeInfo.Type.IsInterface)
+ {
+ ignoreCurrentProperty =
+ // Does the current property have `JsonIgnoreAttribute`?
+ jsonPropertyInfo.IsIgnored ||
+ // Is the current property hidden by the previously cached property
+ // (with `new` keyword, or by overriding)?
+ other.MemberName == memberName ||
+ // Was a property with the same CLR name ignored? That property hid the current property,
+ // thus, if it was ignored, the current property should be ignored too.
+ state.IgnoredProperties?.ContainsKey(memberName) == true;
+ }
+ else
+ {
+ // Unlike classes, interface hierarchies reject all naming conflicts for non-ignored properties.
+ // Conflicts like this are possible in two cases:
+ // 1. Diamond ambiguity in property names, or
+ // 2. Linear interface hierarchies that use properties with DIMs.
+ //
+ // Diamond ambiguities are not supported. Assuming there is demand, we might consider
+ // adding support for DIMs in the future, however that would require adding more APIs
+ // for the case of source gen.
+
+ ignoreCurrentProperty = jsonPropertyInfo.IsIgnored;
+ }
+
+ if (!ignoreCurrentProperty)
+ {
+ ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(_jsonTypeInfo.Type, jsonPropertyInfo.Name);
+ }
+ }
+ }
+
+ if (jsonPropertyInfo.IsIgnored)
+ {
+ (state.IgnoredProperties ??= new()).Add(memberName, jsonPropertyInfo);
+ }
+ }
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
return this.ClassWithCustomConverter;
}
+ if (type == typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList))
+ {
+ return this.MyLinkedList;
+ }
+
return null!;
}
}
-// <auto-generated/>
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Source files represent a source generated JsonSerializerContext as produced by the .NET 6 SDK.
+// Used to validate correctness of contexts generated by previous SDKs against the current System.Text.Json runtime components.
+// Unless absolutely necessary DO NOT MODIFY any of these files -- it would invalidate the purpose of the regression tests.
+
+// <auto-generated/>
#nullable enable
// Suppress warnings about [Obsolete] member usage in generated code.
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Source files represent a source generated JsonSerializerContext as produced by the .NET 6 SDK.
+// Used to validate correctness of contexts generated by previous SDKs against the current System.Text.Json runtime components.
+// Unless absolutely necessary DO NOT MODIFY any of these files -- it would invalidate the purpose of the regression tests.
+
+// <auto-generated/>
+#nullable enable
+
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+
+namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
+{
+ public partial class Net60GeneratedContext
+ {
+ private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>? _MyLinkedList;
+ public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList> MyLinkedList
+ {
+ get
+ {
+ if (_MyLinkedList == null)
+ {
+ global::System.Text.Json.Serialization.JsonConverter? customConverter;
+ if (Options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList))) != null)
+ {
+ _MyLinkedList = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>(Options, customConverter);
+ }
+ else
+ {
+ global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>()
+ {
+ ObjectCreator = null,
+ ObjectWithParameterizedConstructorCreator = static (args) => new global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList((global::System.Int32)args[0], (global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)args[1]),
+ PropertyMetadataInitializer = MyLinkedListPropInit,
+ ConstructorParameterMetadataInitializer = MyLinkedListCtorParamInit,
+ NumberHandling = default,
+ SerializeHandler = MyLinkedListSerializeHandler
+ };
+
+ _MyLinkedList = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>(Options, objectInfo);
+ }
+ }
+
+ return _MyLinkedList;
+ }
+ }
+
+ private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] MyLinkedListPropInit(global::System.Text.Json.Serialization.JsonSerializerContext context)
+ {
+ global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.Net60GeneratedContext jsonContext = (global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.Net60GeneratedContext)context;
+ global::System.Text.Json.JsonSerializerOptions options = context.Options;
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[2];
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32>()
+ {
+ IsProperty = true,
+ IsPublic = true,
+ IsVirtual = false,
+ DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList),
+ PropertyTypeInfo = jsonContext.Int32,
+ Converter = null,
+ Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Value,
+ Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Value = value!,
+ IgnoreCondition = null,
+ HasJsonInclude = false,
+ IsExtensionData = false,
+ NumberHandling = default,
+ PropertyName = "Value",
+ JsonPropertyName = null
+ };
+
+ properties[0] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Int32>(options, info0);
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList> info1 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>()
+ {
+ IsProperty = true,
+ IsPublic = true,
+ IsVirtual = false,
+ DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList),
+ PropertyTypeInfo = jsonContext.MyLinkedList,
+ Converter = null,
+ Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Nested!,
+ Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)obj).Nested = value!,
+ IgnoreCondition = null,
+ HasJsonInclude = false,
+ IsExtensionData = false,
+ NumberHandling = default,
+ PropertyName = "Nested",
+ JsonPropertyName = null
+ };
+
+ properties[1] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList>(options, info1);
+
+ return properties;
+ }
+
+ private static void MyLinkedListSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList? value)
+ {
+ if (value == null)
+ {
+ writer.WriteNullValue();
+ return;
+ }
+
+ writer.WriteStartObject();
+ writer.WriteNumber(PropName_Value, value.Value);
+ writer.WritePropertyName(PropName_Nested);
+ MyLinkedListSerializeHandler(writer, value.Nested!);
+
+ writer.WriteEndObject();
+ }
+
+ private static global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] MyLinkedListCtorParamInit()
+ {
+ global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] parameters = new global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[2];
+ global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues info;
+
+ info = new()
+ {
+ Name = "value",
+ ParameterType = typeof(global::System.Int32),
+ Position = 0,
+ HasDefaultValue = false,
+ DefaultValue = default(global::System.Int32)
+ };
+ parameters[0] = info;
+
+ info = new()
+ {
+ Name = "nested",
+ ParameterType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList),
+ Position = 1,
+ HasDefaultValue = false,
+ DefaultValue = default(global::System.Text.Json.Tests.SourceGenRegressionTests.Net60.MyLinkedList)
+ };
+ parameters[1] = info;
+
+ return parameters;
+ }
+ }
+}
private static readonly global::System.Text.Json.JsonEncodedText PropName_SummaryWords = global::System.Text.Json.JsonEncodedText.Encode("SummaryWords");
private static readonly global::System.Text.Json.JsonEncodedText PropName_High = global::System.Text.Json.JsonEncodedText.Encode("High");
private static readonly global::System.Text.Json.JsonEncodedText PropName_Low = global::System.Text.Json.JsonEncodedText.Encode("Low");
+ private static readonly global::System.Text.Json.JsonEncodedText PropName_Value = global::System.Text.Json.JsonEncodedText.Encode("Value");
+ private static readonly global::System.Text.Json.JsonEncodedText PropName_Nested = global::System.Text.Json.JsonEncodedText.Encode("Nested");
}
}
{
//[JsonSerializable(typeof(WeatherForecastWithPOCOs))]
//[JsonSerializable(typeof(ClassWithCustomConverter))]
+ //[JsonSerializable(typeof(MyLinkedList))]
public partial class Net60GeneratedContext : JsonSerializerContext { }
public class WeatherForecastWithPOCOs
public int Low { get; set; }
}
+ public class MyLinkedList
+ {
+ public MyLinkedList(int value, MyLinkedList? nested)
+ {
+ Value = value;
+ Nested = nested;
+ }
+
+ public int Value { get; set; }
+ public MyLinkedList? Nested { get; set; }
+ }
+
[JsonConverter(typeof(CustomConverter))]
public class ClassWithCustomConverter
{
namespace System.Text.Json.Tests.SourceGenRegressionTests.Net60
{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "6.0.6.21309")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "6.0.8.17311")]
public partial class Net60GeneratedContext
{
}
[Fact]
+ public static void SupportsRecursiveTypeSerialization()
+ {
+ JsonTypeInfo<MyLinkedList> jsonTypeInfo = Net60GeneratedContext.Default.MyLinkedList;
+
+ MyLinkedList linkedList = new(
+ value: 0,
+ nested: new(
+ value: 1,
+ nested: new(
+ value: 2,
+ nested: null)));
+
+ string json = JsonSerializer.Serialize(linkedList, jsonTypeInfo);
+ Assert.Equal("""{"Value":0,"Nested":{"Value":1,"Nested":{"Value":2,"Nested":null}}}""", json);
+
+ linkedList = JsonSerializer.Deserialize(json, jsonTypeInfo);
+ Assert.Equal(2, linkedList.Nested.Nested.Value);
+ }
+
+ [Fact]
public static void CombinedContexts_ThrowsInvalidOperationException()
{
var options = new JsonSerializerOptions
return this.ClassWithCustomConverter;
}
+ if (type == typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList))
+ {
+ return this.MyLinkedList;
+ }
+
return null!;
}
return Create_ClassWithCustomConverter(options, makeReadOnly: false);
}
+ if (type == typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList))
+ {
+ return Create_MyLinkedList(options, makeReadOnly: false);
+ }
+
return null;
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Source files represent a source generated JsonSerializerContext as produced by the .NET 7 SDK.
+// Used to validate correctness of contexts generated by previous SDKs against the current System.Text.Json runtime components.
+// Unless absolutely necessary DO NOT MODIFY any of these files -- it would invalidate the purpose of the regression tests.
+
+// <auto-generated/>
+
+#nullable enable annotations
+#nullable disable warnings
+
+// Suppress warnings about [Obsolete] member usage in generated code.
+#pragma warning disable CS0618
+
+namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
+{
+ public partial class Net70GeneratedContext
+ {
+ private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>? _MyLinkedList;
+ /// <summary>
+ /// Defines the source generated JSON serialization contract metadata for a given type.
+ /// </summary>
+ public global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> MyLinkedList
+ {
+ get => _MyLinkedList ??= Create_MyLinkedList(Options, makeReadOnly: true);
+ }
+
+ private global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> Create_MyLinkedList(global::System.Text.Json.JsonSerializerOptions options, bool makeReadOnly)
+ {
+ global::System.Text.Json.Serialization.Metadata.JsonTypeInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>? jsonTypeInfo = null;
+ global::System.Text.Json.Serialization.JsonConverter? customConverter;
+ if (options.Converters.Count > 0 && (customConverter = GetRuntimeProvidedCustomConverter(options, typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList))) != null)
+ {
+ jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateValueInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>(options, customConverter);
+ }
+ else
+ {
+ global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> objectInfo = new global::System.Text.Json.Serialization.Metadata.JsonObjectInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>()
+ {
+ ObjectCreator = null,
+ ObjectWithParameterizedConstructorCreator = static (args) => new global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList((global::System.Int32)args[0], (global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)args[1]),
+ PropertyMetadataInitializer = _ => MyLinkedListPropInit(options),
+ ConstructorParameterMetadataInitializer = MyLinkedListCtorParamInit,
+ NumberHandling = default,
+ SerializeHandler = MyLinkedListSerializeHandler
+ };
+
+ jsonTypeInfo = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreateObjectInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>(options, objectInfo);
+ }
+
+ if (makeReadOnly)
+ {
+ jsonTypeInfo.MakeReadOnly();
+ }
+
+ return jsonTypeInfo;
+ }
+
+ private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] MyLinkedListPropInit(global::System.Text.Json.JsonSerializerOptions options)
+ {
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[2];
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32> info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Int32>()
+ {
+ IsProperty = true,
+ IsPublic = true,
+ IsVirtual = false,
+ DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList),
+ Converter = null,
+ Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Value,
+ Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Value = value!,
+ IgnoreCondition = null,
+ HasJsonInclude = false,
+ IsExtensionData = false,
+ NumberHandling = default,
+ PropertyName = "Value",
+ JsonPropertyName = null
+ };
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo0 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Int32>(options, info0);
+ properties[0] = propertyInfo0;
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList> info1 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>()
+ {
+ IsProperty = true,
+ IsPublic = true,
+ IsVirtual = false,
+ DeclaringType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList),
+ Converter = null,
+ Getter = static (obj) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Nested!,
+ Setter = static (obj, value) => ((global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)obj).Nested = value!,
+ IgnoreCondition = null,
+ HasJsonInclude = false,
+ IsExtensionData = false,
+ NumberHandling = default,
+ PropertyName = "Nested",
+ JsonPropertyName = null
+ };
+
+ global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo propertyInfo1 = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList>(options, info1);
+ properties[1] = propertyInfo1;
+
+ return properties;
+ }
+
+ // Intentionally not a static method because we create a delegate to it. Invoking delegates to instance
+ // methods is almost as fast as virtual calls. Static methods need to go through a shuffle thunk.
+ private void MyLinkedListSerializeHandler(global::System.Text.Json.Utf8JsonWriter writer, global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList? value)
+ {
+ if (value == null)
+ {
+ writer.WriteNullValue();
+ return;
+ }
+
+ writer.WriteStartObject();
+ writer.WriteNumber(PropName_Value, value.Value);
+ writer.WritePropertyName(PropName_Nested);
+ MyLinkedListSerializeHandler(writer, value.Nested!);
+
+ writer.WriteEndObject();
+ }
+
+ private static global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] MyLinkedListCtorParamInit()
+ {
+ global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[] parameters = new global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues[2];
+ global::System.Text.Json.Serialization.Metadata.JsonParameterInfoValues info;
+
+ info = new()
+ {
+ Name = "value",
+ ParameterType = typeof(global::System.Int32),
+ Position = 0,
+ HasDefaultValue = false,
+ DefaultValue = default(global::System.Int32)
+ };
+ parameters[0] = info;
+
+ info = new()
+ {
+ Name = "nested",
+ ParameterType = typeof(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList),
+ Position = 1,
+ HasDefaultValue = false,
+ DefaultValue = default(global::System.Text.Json.Tests.SourceGenRegressionTests.Net70.MyLinkedList)
+ };
+ parameters[1] = info;
+
+ return parameters;
+ }
+ }
+}
private static readonly global::System.Text.Json.JsonEncodedText PropName_SummaryWords = global::System.Text.Json.JsonEncodedText.Encode("SummaryWords");
private static readonly global::System.Text.Json.JsonEncodedText PropName_High = global::System.Text.Json.JsonEncodedText.Encode("High");
private static readonly global::System.Text.Json.JsonEncodedText PropName_Low = global::System.Text.Json.JsonEncodedText.Encode("Low");
+ private static readonly global::System.Text.Json.JsonEncodedText PropName_Value = global::System.Text.Json.JsonEncodedText.Encode("Value");
+ private static readonly global::System.Text.Json.JsonEncodedText PropName_Nested = global::System.Text.Json.JsonEncodedText.Encode("Nested");
}
}
{
//[JsonSerializable(typeof(WeatherForecastWithPOCOs))]
//[JsonSerializable(typeof(ClassWithCustomConverter))]
+ //[JsonSerializable(typeof(MyLinkedList))]
public partial class Net70GeneratedContext : JsonSerializerContext { }
public class WeatherForecastWithPOCOs
public int Low { get; set; }
}
+ public class MyLinkedList
+ {
+ public MyLinkedList(int value, MyLinkedList? nested)
+ {
+ Value = value;
+ Nested = nested;
+ }
+
+ public int Value { get; set; }
+ public MyLinkedList? Nested { get; set; }
+ }
+
[JsonConverter(typeof(CustomConverter))]
public class ClassWithCustomConverter
{
namespace System.Text.Json.Tests.SourceGenRegressionTests.Net70
{
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "7.0.7.1805")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.Json.SourceGeneration", "7.0.8.17405")]
public partial class Net70GeneratedContext
{
}
[Fact]
+ public static void SupportsRecursiveTypeSerialization()
+ {
+ JsonTypeInfo<MyLinkedList> jsonTypeInfo = Net70GeneratedContext.Default.MyLinkedList;
+
+ MyLinkedList linkedList = new(
+ value: 0,
+ nested: new(
+ value: 1,
+ nested: new(
+ value: 2,
+ nested: null)));
+
+ string json = JsonSerializer.Serialize(linkedList, jsonTypeInfo);
+ Assert.Equal("""{"Value":0,"Nested":{"Value":1,"Nested":{"Value":2,"Nested":null}}}""", json);
+
+ linkedList = JsonSerializer.Deserialize(json, jsonTypeInfo);
+ Assert.Equal(2, linkedList.Nested.Nested.Value);
+ }
+
+ [Fact]
public static void CombinedContexts_WorksAsExpected()
{
var options = new JsonSerializerOptions
<Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.HighLowTemps.g.cs" />
<Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.Int32.g.cs" />
<Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.ListDateTimeOffset.g.cs" />
+ <Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.MyLinkedList.g.cs" />
<Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.PropertyNames.g.cs" />
<Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.String.g.cs" />
<Compile Include="SourceGenRegressionTests\Net60\Net60GeneratedContext.StringArray.g.cs" />
<Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.HighLowTemps.g.cs" />
<Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.Int32.g.cs" />
<Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.ListDateTimeOffset.g.cs" />
+ <Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.MyLinkedList.g.cs" />
<Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.PropertyNames.g.cs" />
<Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.String.g.cs" />
<Compile Include="SourceGenRegressionTests\Net70\Net70GeneratedContext.StringArray.g.cs" />