From 89dea8768f22c1ac1c14e521756fb034e0ea5924 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Mon, 6 Jun 2022 15:17:09 +0200 Subject: [PATCH] Implied resolver implementation --- .../Serialization/JsonSerializerContext.cs | 1 + .../JsonSerializerOptions.Caching.cs | 8 ++---- .../Serialization/JsonSerializerOptions.cs | 27 ++++++++++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs index 6d87444a463c28..5833fc63d83749 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs @@ -106,6 +106,7 @@ protected JsonSerializerContext(JsonSerializerOptions? options, bool bindOptions else { _options = options; + options.SetImpliedResolver(this); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.cs index a0a2de2adaded2..b1bb738d2b207c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.cs @@ -286,7 +286,7 @@ public bool Equals(JsonSerializerOptions? left, JsonSerializerOptions? right) left._includeFields == right._includeFields && left._propertyNameCaseInsensitive == right._propertyNameCaseInsensitive && left._writeIndented == right._writeIndented && - NormalizeResolver(left._typeInfoResolver) == NormalizeResolver(right._typeInfoResolver) && + left.NormalizedResolver == right.NormalizedResolver && CompareLists(left._converters, right._converters) && CompareLists(left._polymorphicTypeConfigurations, right._polymorphicTypeConfigurations); @@ -331,7 +331,7 @@ public int GetHashCode(JsonSerializerOptions options) hc.Add(options._includeFields); hc.Add(options._propertyNameCaseInsensitive); hc.Add(options._writeIndented); - hc.Add(NormalizeResolver(options._typeInfoResolver)); + hc.Add(options.NormalizedResolver); GetHashCode(ref hc, options._converters); GetHashCode(ref hc, options._polymorphicTypeConfigurations); @@ -346,10 +346,6 @@ static void GetHashCode(ref HashCode hc, ConfigurationList list) return hc.ToHashCode(); } - // An options instance might be locked but not initialized for reflection serialization yet. - private static IJsonTypeInfoResolver? NormalizeResolver(IJsonTypeInfoResolver? resolver) - => resolver ?? DefaultJsonTypeInfoResolver.DefaultInstance; - #if !NETCOREAPP /// /// Polyfill for System.HashCode. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index 45ae91d6abd886..13243dd177c118 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -36,6 +36,12 @@ public sealed partial class JsonSerializerOptions // For any new option added, adding it to the options copied in the copy constructor below must be considered. private IJsonTypeInfoResolver? _typeInfoResolver; + // If _typeInfoResolver is not set, we try to use _impliedTypeInfoResolver, if that is not set we use DefaultJsonTypeInfoResolver + private IJsonTypeInfoResolver? _impliedTypeInfoResolver; + + // Effective resolver which doesn't root and can be used for GetHashCode or Equals + internal IJsonTypeInfoResolver? NormalizedResolver => _typeInfoResolver ?? _impliedTypeInfoResolver ?? DefaultJsonTypeInfoResolver.DefaultInstance; + private MemberAccessor? _memberAccessorStrategy; private JsonNamingPolicy? _dictionaryKeyPolicy; private JsonNamingPolicy? _jsonPropertyNamingPolicy; @@ -173,7 +179,7 @@ public IJsonTypeInfoResolver TypeInfoResolver [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] get { - return _typeInfoResolver ?? DefaultJsonTypeInfoResolver.RootDefaultInstance(); + return NormalizedResolver ?? DefaultJsonTypeInfoResolver.RootDefaultInstance(); } set { @@ -619,6 +625,18 @@ internal MemberAccessor MemberAccessorStrategy } } + private bool _moreThanOneImpliedResolverSet; + + // This is used only by JsonSerializerContext ctor which takes options. + // For backward compatibility we need to use context as our resolver + internal void SetImpliedResolver(IJsonTypeInfoResolver resolver) + { + VerifyMutable(); + + _moreThanOneImpliedResolverSet = _impliedTypeInfoResolver != null; + _impliedTypeInfoResolver = resolver; + } + internal bool IsInitializedForReflectionSerializer { get; private set; } // Effective resolver, populated when enacting reflection-based fallback // Should not be taken into account when calculating options equality. @@ -631,6 +649,13 @@ internal MemberAccessor MemberAccessorStrategy [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] internal void InitializeForReflectionSerializer() { + if (_typeInfoResolver == null && _moreThanOneImpliedResolverSet) + { + ThrowHelper.ThrowInvalidOperationException_SerializerContextOptionsImmutable(); + } + + _typeInfoResolver ??= _impliedTypeInfoResolver; + if (_typeInfoResolver is JsonSerializerContext ctx) { // .NET 6 backward compatibility; use fallback to reflection serialization