Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ReferenceHandler to JsonSourceGenerationOptions #109174

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/libraries/System.Text.Json/Common/JsonKnownReferenceHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Text.Json.Serialization
{
/// <summary>
/// The <see cref="ReferenceHandler"/> to be used at run time.
/// </summary>
public enum JsonKnownReferenceHandler
{
/// <summary>
/// Specifies that circular references should throw exceptions.
/// </summary>
Unspecified = 0,

/// <summary>
/// Specifies that the built-in <see cref="ReferenceHandler.Preserve"/> be used to handle references.
/// </summary>
Preserve = 1,

/// <summary>
/// Specifies that the built-in <see cref="ReferenceHandler.IgnoreCycles"/> be used to ignore cyclic references.
/// </summary>
IgnoreCycles = 2,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ public JsonSourceGenerationOptionsAttribute(JsonSerializerDefaults defaults)
/// </summary>
public JsonCommentHandling ReadCommentHandling { get; set; }

/// <summary>
/// Specifies the default value of <see cref="JsonSerializerOptions.ReferenceHandler"/> when set.
/// </summary>
public JsonKnownReferenceHandler ReferenceHandler { get; set; }

/// <summary>
/// Specifies the default value of <see cref="JsonSerializerOptions.RespectNullableAnnotations"/> when set.
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ private sealed partial class Emitter
private const string OptionsLocalVariableName = "options";
private const string ValueVarName = "value";
private const string WriterVarName = "writer";
private const string PreserveReferenceHandlerPropertyName = "Preserve";
private const string IgnoreCyclesReferenceHandlerPropertyName = "IgnoreCycles";

private static readonly AssemblyName s_assemblyName = typeof(Emitter).Assembly.GetName();

Expand Down Expand Up @@ -70,6 +72,7 @@ private sealed partial class Emitter
private const string JsonPropertyInfoValuesTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues";
private const string JsonTypeInfoTypeRef = "global::System.Text.Json.Serialization.Metadata.JsonTypeInfo";
private const string JsonTypeInfoResolverTypeRef = "global::System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver";
private const string ReferenceHandlerTypeRef = "global::System.Text.Json.Serialization.ReferenceHandler";
private const string EmptyTypeArray = "global::System.Array.Empty<global::System.Type>()";

/// <summary>
Expand Down Expand Up @@ -1246,6 +1249,9 @@ private static void GetLogicForDefaultSerializerOptionsInit(SourceGenerationOpti
if (optionsSpec.ReadCommentHandling is JsonCommentHandling readCommentHandling)
writer.WriteLine($"ReadCommentHandling = {FormatCommentHandling(readCommentHandling)},");

if (optionsSpec.ReferenceHandler is JsonKnownReferenceHandler referenceHandler)
writer.WriteLine($"ReferenceHandler = {FormatReferenceHandler(referenceHandler)},");

if (optionsSpec.UnknownTypeHandling is JsonUnknownTypeHandling unknownTypeHandling)
writer.WriteLine($"UnknownTypeHandling = {FormatUnknownTypeHandling(unknownTypeHandling)},");

Expand Down Expand Up @@ -1280,6 +1286,20 @@ static string FormatNamingPolicy(JsonKnownNamingPolicy knownNamingPolicy)
? $"{JsonNamingPolicyTypeRef}.{policyName}"
: "null";
}

static string FormatReferenceHandler(JsonKnownReferenceHandler referenceHandler)
{
string? referenceHandlerName = referenceHandler switch
{
JsonKnownReferenceHandler.Preserve => PreserveReferenceHandlerPropertyName,
JsonKnownReferenceHandler.IgnoreCycles => IgnoreCyclesReferenceHandlerPropertyName,
_ => null,
};

return referenceHandlerName != null
? $"{ReferenceHandlerTypeRef}.{referenceHandlerName}"
: "null";
}
}

private static void GenerateConverterHelpers(SourceWriter writer, bool emitGetConverterForNullablePropertyMethod)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ private SourceGenerationOptionsSpec ParseJsonSourceGenerationOptionsAttribute(IN
bool? propertyNameCaseInsensitive = null;
JsonKnownNamingPolicy? propertyNamingPolicy = null;
JsonCommentHandling? readCommentHandling = null;
JsonKnownReferenceHandler? referenceHandler = null;
JsonUnknownTypeHandling? unknownTypeHandling = null;
JsonUnmappedMemberHandling? unmappedMemberHandling = null;
bool? useStringEnumConverter = null;
Expand Down Expand Up @@ -379,6 +380,10 @@ private SourceGenerationOptionsSpec ParseJsonSourceGenerationOptionsAttribute(IN
readCommentHandling = (JsonCommentHandling)namedArg.Value.Value!;
break;

case nameof(JsonSourceGenerationOptionsAttribute.ReferenceHandler):
referenceHandler = (JsonKnownReferenceHandler)namedArg.Value.Value!;
break;

case nameof(JsonSourceGenerationOptionsAttribute.UnknownTypeHandling):
unknownTypeHandling = (JsonUnknownTypeHandling)namedArg.Value.Value!;
break;
Expand Down Expand Up @@ -434,6 +439,7 @@ private SourceGenerationOptionsSpec ParseJsonSourceGenerationOptionsAttribute(IN
PropertyNameCaseInsensitive = propertyNameCaseInsensitive,
PropertyNamingPolicy = propertyNamingPolicy,
ReadCommentHandling = readCommentHandling,
ReferenceHandler = referenceHandler,
UnknownTypeHandling = unknownTypeHandling,
UnmappedMemberHandling = unmappedMemberHandling,
UseStringEnumConverter = useStringEnumConverter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public sealed record SourceGenerationOptionsSpec

public required JsonCommentHandling? ReadCommentHandling { get; init; }

public required JsonKnownReferenceHandler? ReferenceHandler { get; init; }

public required JsonUnknownTypeHandling? UnknownTypeHandling { get; init; }

public required JsonUnmappedMemberHandling? UnmappedMemberHandling { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<Compile Include="..\Common\JsonKebabCaseLowerNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKebabCaseLowerNamingPolicy.cs" />
<Compile Include="..\Common\JsonKebabCaseUpperNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKebabCaseUpperNamingPolicy.cs" />
<Compile Include="..\Common\JsonKnownNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKnownNamingPolicy.cs" />
<Compile Include="..\Common\JsonKnownReferenceHandler.cs" Link="Common\System\Text\Json\Serialization\JsonKnownReferenceHandler.cs" />
<Compile Include="..\Common\JsonNumberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonNumberHandling.cs" />
<Compile Include="..\Common\JsonObjectCreationHandling.cs" Link="Common\System\Text\Json\Serialization\JsonObjectCreationHandling.cs" />
<Compile Include="..\Common\JsonSeparatorNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonSeparatorNamingPolicy.cs" />
Expand Down
7 changes: 7 additions & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,12 @@ public enum JsonKnownNamingPolicy
KebabCaseLower = 4,
KebabCaseUpper = 5,
}
public enum JsonKnownReferenceHandler
{
Unspecified = 0,
Preserve = 1,
IgnoreCycles = 2,
}
public sealed partial class JsonNumberEnumConverter<TEnum> : System.Text.Json.Serialization.JsonConverterFactory where TEnum : struct, System.Enum
{
public JsonNumberEnumConverter() { }
Expand Down Expand Up @@ -1143,6 +1149,7 @@ public JsonSourceGenerationOptionsAttribute(System.Text.Json.JsonSerializerDefau
public bool PropertyNameCaseInsensitive { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonKnownNamingPolicy PropertyNamingPolicy { get { throw null; } set { } }
public System.Text.Json.JsonCommentHandling ReadCommentHandling { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonKnownReferenceHandler ReferenceHandler { get { throw null; } set { } }
public bool RespectNullableAnnotations { get { throw null; } set { } }
public bool RespectRequiredConstructorParameters { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonUnknownTypeHandling UnknownTypeHandling { get { throw null; } set { } }
Expand Down
6 changes: 3 additions & 3 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
Expand Down Expand Up @@ -33,9 +33,9 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="..\Common\JsonKebabCaseLowerNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKebabCaseLowerNamingPolicy.cs" />
<Compile Include="..\Common\JsonKebabCaseUpperNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKebabCaseUpperNamingPolicy.cs" />
<Compile Include="..\Common\JsonKnownNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKnownNamingPolicy.cs" />
<Compile Include="..\Common\JsonKnownReferenceHandler.cs" Link="Common\System\Text\Json\Serialization\JsonKnownReferenceHandler.cs" />
<Compile Include="..\Common\JsonNumberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonNumberHandling.cs" />
<Compile Include="..\Common\JsonObjectCreationHandling.cs" Link="Common\System\Text\Json\Serialization\JsonObjectCreationHandling.cs" />
<Compile Include="..\Common\JsonUnmappedMemberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonUnmappedMemberHandling.cs" />
<Compile Include="..\Common\JsonSeparatorNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonSeparatorNamingPolicy.cs" />
<Compile Include="..\Common\JsonSerializableAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSerializableAttribute.cs" />
<Compile Include="..\Common\JsonSerializerDefaults.cs" Link="Common\System\Text\Json\Serialization\JsonSerializerDefaults.cs" />
Expand All @@ -44,6 +44,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="..\Common\JsonSourceGenerationMode.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationMode.cs" />
<Compile Include="..\Common\JsonSourceGenerationOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationOptionsAttribute.cs" />
<Compile Include="..\Common\JsonUnknownTypeHandling.cs" Link="Common\System\Text\Json\Serialization\JsonUnknownTypeHandling.cs" />
<Compile Include="..\Common\JsonUnmappedMemberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonUnmappedMemberHandling.cs" />
<Compile Include="..\Common\ReflectionExtensions.cs" Link="Common\System\Text\Json\Serialization\ReflectionExtensions.cs" />
<Compile Include="..\Common\ThrowHelper.cs" Link="Common\System\Text\Json\ThrowHelper.cs" />
<Compile Include="System\Runtime\InteropServices\JsonMarshal.cs" />
Expand Down Expand Up @@ -295,7 +296,6 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="System\Text\Json\Serialization\ReadStackFrame.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceHandler.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceHandlerOfT.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceHandlingStrategy.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceResolver.cs" />
<Compile Include="System\Text\Json\Serialization\StackFrameObjectState.cs" />
<Compile Include="System\Text\Json\Serialization\StackFramePropertyState.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ internal override bool OnTryRead(
if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
{
Debug.Assert(state.ReferenceId != null);
Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve);
Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
Debug.Assert(state.Current.ReturnValue is TCollection);
state.ReferenceResolver.AddReference(state.ReferenceId, state.Current.ReturnValue);
state.ReferenceId = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ internal sealed override bool OnTryRead(
if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
{
Debug.Assert(state.ReferenceId != null);
Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve);
Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
Debug.Assert(state.Current.ReturnValue is TDictionary);
state.ReferenceResolver.AddReference(state.ReferenceId, state.Current.ReturnValue);
state.ReferenceId = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
JsonElement element = JsonElement.ParseValue(ref reader);

// Edge case where we want to lookup for a reference when parsing into typeof(object)
if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve &&
if (options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve &&
JsonSerializer.TryHandleReferenceFromJsonElement(ref reader, ref state, element, out referenceValue))
{
value = referenceValue;
Expand All @@ -134,7 +134,7 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,

JsonNode? node = JsonNodeConverter.Instance.Read(ref reader, typeToConvert, options);

if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve &&
if (options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve &&
JsonSerializer.TryHandleReferenceFromJsonNode(ref reader, ref state, node, out referenceValue))
{
value = referenceValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
{
Debug.Assert(state.ReferenceId != null);
Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve);
Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
state.ReferenceResolver.AddReference(state.ReferenceId, obj);
state.ReferenceId = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo
if ((state.Current.MetadataPropertyNames & MetadataPropertyName.Id) != 0)
{
Debug.Assert(state.ReferenceId != null);
Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve);
Debug.Assert(options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Preserve);
state.ReferenceResolver.AddReference(state.ReferenceId, obj);
state.ReferenceId = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace System.Text.Json.Serialization
{
internal sealed class IgnoreReferenceHandler : ReferenceHandler
{
public IgnoreReferenceHandler() => HandlingStrategy = ReferenceHandlingStrategy.IgnoreCycles;
public IgnoreReferenceHandler() => HandlingStrategy = JsonKnownReferenceHandler.IgnoreCycles;

public override ReferenceResolver CreateResolver() => new IgnoreReferenceResolver();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ internal bool TryHandleSerializedObjectReference(Utf8JsonWriter writer, object v

switch (options.ReferenceHandlingStrategy)
{
case ReferenceHandlingStrategy.IgnoreCycles:
case JsonKnownReferenceHandler.IgnoreCycles:
ReferenceResolver resolver = state.ReferenceResolver;
if (resolver.ContainsReferenceForCycleDetection(value))
{
Expand All @@ -158,7 +158,7 @@ internal bool TryHandleSerializedObjectReference(Utf8JsonWriter writer, object v
state.Current.IsPushedReferenceForCycleDetection = state.CurrentDepth > 0;
break;

case ReferenceHandlingStrategy.Preserve:
case JsonKnownReferenceHandler.Preserve:
bool canHaveIdMetadata = polymorphicConverter?.CanHaveMetadata ?? CanHaveMetadata;
if (canHaveIdMetadata && JsonSerializer.TryGetReferenceForValue(value, ref state, writer))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ value is not null &&
ResolvePolymorphicConverter(value, jsonTypeInfo, options, ref state) :
null;

if (!isContinuation && options.ReferenceHandlingStrategy != ReferenceHandlingStrategy.None &&
if (!isContinuation && options.ReferenceHandlingStrategy != JsonKnownReferenceHandler.Unspecified &&
TryHandleSerializedObjectReference(writer, value, options, polymorphicConverter, ref state))
{
// The reference handler wrote reference metadata, serialization complete.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ options.Encoder is null &&
// Disallow custom number handling we'd need to honor when writing.
// AllowReadingFromString and Strict are fine since there's no action to take when writing.
!JsonHelpers.RequiresSpecialNumberHandlingOnWrite(options.NumberHandling) &&
options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.None &&
options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.Unspecified &&
#pragma warning disable SYSLIB0020
!options.IgnoreNullValues && // This property is obsolete.
#pragma warning restore SYSLIB0020
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ public ReferenceHandler? ReferenceHandler
{
VerifyMutable();
_referenceHandler = value;
ReferenceHandlingStrategy = value?.HandlingStrategy ?? ReferenceHandlingStrategy.None;
ReferenceHandlingStrategy = value?.HandlingStrategy ?? JsonKnownReferenceHandler.Unspecified;
}
}

Expand Down Expand Up @@ -853,7 +853,7 @@ internal bool CanUseFastPathSerializationLogic
private bool? _canUseFastPathSerializationLogic;

// The cached value used to determine if ReferenceHandler should use Preserve or IgnoreCycles semantics or None of them.
internal ReferenceHandlingStrategy ReferenceHandlingStrategy = ReferenceHandlingStrategy.None;
internal JsonKnownReferenceHandler ReferenceHandlingStrategy = JsonKnownReferenceHandler.Unspecified;

/// <summary>
/// Specifies whether the current instance has been locked for user modification.
Expand Down
Loading
Loading