< Summary

Information
Class: System.Text.Json.Serialization.Metadata.JsonPropertyInfo<T>
Assembly: System.Text.Json
File(s): C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\JsonPropertyInfoOfT.cs
Line coverage
16%
Covered lines: 47
Uncovered lines: 240
Coverable lines: 287
Total lines: 458
Line coverage: 16.3%
Branch coverage
12%
Covered branches: 22
Total branches: 179
Branch coverage: 12.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\JsonPropertyInfoOfT.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Reflection;
 8
 9namespace System.Text.Json.Serialization.Metadata
 10{
 11    /// <summary>
 12    /// Represents a strongly-typed property to prevent boxing and to create a direct delegate to the getter\setter.
 13    /// </summary>
 14    internal sealed class JsonPropertyInfo<T> : JsonPropertyInfo
 15    {
 16        private Func<object, T>? _typedGet;
 17        private Action<object, T>? _typedSet;
 18
 19        internal JsonPropertyInfo(Type declaringType, JsonTypeInfo? declaringTypeInfo, JsonSerializerOptions options)
 2004820            : base(declaringType, propertyType: typeof(T), declaringTypeInfo, options)
 2004821        {
 2004822        }
 23
 24        internal new Func<object, T>? Get
 25        {
 026            get => _typedGet;
 779427            set => SetGetter(value);
 28        }
 29
 30        internal new Action<object, T>? Set
 31        {
 032            get => _typedSet;
 779433            set => SetSetter(value);
 34        }
 35
 36        private protected override void SetGetter(Delegate? getter)
 779437        {
 779438            Debug.Assert(getter is null or Func<object, object?> or Func<object, T>);
 779439            Debug.Assert(!IsConfigured);
 40
 779441            if (getter is null)
 042            {
 043                _typedGet = null;
 044                _untypedGet = null;
 045            }
 779446            else if (getter is Func<object, T> typedGetter)
 779447            {
 779448                _typedGet = typedGetter;
 779449                _untypedGet = getter is Func<object, object?> untypedGet ? untypedGet : obj => typedGetter(obj);
 779450            }
 51            else
 052            {
 053                Func<object, object?> untypedGet = (Func<object, object?>)getter;
 054                _typedGet = (obj => (T)untypedGet(obj)!);
 055                _untypedGet = untypedGet;
 056            }
 779457        }
 58
 59        private protected override void SetSetter(Delegate? setter)
 779460        {
 779461            Debug.Assert(setter is null or Action<object, object?> or Action<object, T>);
 779462            Debug.Assert(!IsConfigured);
 63
 779464            if (setter is null)
 065            {
 066                _typedSet = null;
 067                _untypedSet = null;
 068            }
 779469            else if (setter is Action<object, T> typedSetter)
 779470            {
 779471                _typedSet = typedSetter;
 779472                _untypedSet = setter is Action<object, object?> untypedSet ? untypedSet : (obj, value) => typedSetter(ob
 779473            }
 74            else
 075            {
 076                Action<object, object?> untypedSet = (Action<object, object?>)setter;
 077                _typedSet = ((obj, value) => untypedSet(obj, value));
 078                _untypedSet = untypedSet;
 079            }
 779480        }
 81
 82        internal new Func<object, T?, bool>? ShouldSerialize
 83        {
 084            get => _shouldSerializeTyped;
 085            set => SetShouldSerialize(value);
 86        }
 87
 88        private Func<object, T?, bool>? _shouldSerializeTyped;
 89
 90        private protected override void SetShouldSerialize(Delegate? predicate)
 091        {
 092            Debug.Assert(predicate is null or Func<object, object?, bool> or Func<object, T?, bool>);
 093            Debug.Assert(!IsConfigured);
 94
 095            if (predicate is null)
 096            {
 097                _shouldSerializeTyped = null;
 098                _shouldSerialize = null;
 099            }
 0100            else if (predicate is Func<object, T?, bool> typedPredicate)
 0101            {
 0102                _shouldSerializeTyped = typedPredicate;
 0103                _shouldSerialize = typedPredicate is Func<object, object?, bool> untypedPredicate ? untypedPredicate : (
 0104            }
 105            else
 0106            {
 0107                Func<object, object?, bool> untypedPredicate = (Func<object, object?, bool>)predicate;
 0108                _shouldSerializeTyped = (obj, value) => untypedPredicate(obj, value);
 0109                _shouldSerialize = untypedPredicate;
 0110            }
 0111        }
 112
 113        internal override object? DefaultValue => default(T);
 33162114        internal override bool PropertyTypeCanBeNull => default(T) is null;
 115        internal override void AddJsonParameterInfo(JsonParameterInfoValues parameterInfoValues)
 4494116        {
 4494117            Debug.Assert(!IsConfigured);
 4494118            Debug.Assert(AssociatedParameter is null);
 119
 4494120            AssociatedParameter = new JsonParameterInfo<T>(parameterInfoValues, this);
 121            // Overwrite the nullability annotation of property setter with the parameter.
 4494122            _isSetNullable = parameterInfoValues.IsNullable;
 123
 4494124            if (Options.RespectRequiredConstructorParameters)
 0125            {
 126                // If the property has been associated with a non-optional parameter, mark it as required.
 0127                _isRequired |= AssociatedParameter.IsRequiredParameter;
 0128            }
 4494129        }
 130
 131        internal new JsonConverter<T> EffectiveConverter
 132        {
 133            get
 0134            {
 0135                Debug.Assert(_typedEffectiveConverter != null);
 0136                return _typedEffectiveConverter;
 0137            }
 138        }
 139
 140        private JsonConverter<T>? _typedEffectiveConverter;
 141
 142        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 143        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 144        internal override void DetermineReflectionPropertyAccessors(MemberInfo memberInfo, bool useNonPublicAccessors)
 7794145            => DefaultJsonTypeInfoResolver.DeterminePropertyAccessors<T>(this, memberInfo, useNonPublicAccessors);
 146
 147        private protected override void DetermineEffectiveConverter(JsonTypeInfo jsonTypeInfo)
 20048148        {
 20048149            Debug.Assert(jsonTypeInfo is JsonTypeInfo<T>);
 150
 20048151            JsonConverter<T> converter =
 20048152                Options.ExpandConverterFactory(CustomConverter, PropertyType) // Expand any property-level custom conver
 20048153                ?.CreateCastingConverter<T>()                                 // Cast to JsonConverter<T>, potentially w
 20048154                ?? ((JsonTypeInfo<T>)jsonTypeInfo).EffectiveConverter;        // Fall back to the effective converter fo
 155
 20048156            _effectiveConverter = converter;
 20048157            _typedEffectiveConverter = converter;
 20048158        }
 159
 160        internal override object? GetValueAsObject(object obj)
 0161        {
 0162            if (IsForTypeInfo)
 0163            {
 0164                return obj;
 165            }
 166
 0167            Debug.Assert(HasGetter);
 0168            return Get!(obj);
 0169        }
 170
 171        internal override bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer)
 0172        {
 0173            T value = Get!(obj);
 174
 0175            if (
 0176#if NET
 0177                !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
 0178#else
 0179                !EffectiveConverter.IsValueType &&
 0180#endif
 0181                Options.ReferenceHandlingStrategy == JsonKnownReferenceHandler.IgnoreCycles &&
 0182                value is not null &&
 0183                !state.IsContinuation &&
 0184                // .NET types that are serialized as JSON primitive values don't need to be tracked for cycle detection 
 0185                EffectiveConverter.ConverterStrategy != ConverterStrategy.Value &&
 0186                state.ReferenceResolver.ContainsReferenceForCycleDetection(value))
 0187            {
 188                // If a reference cycle is detected, treat value as null.
 0189                value = default!;
 0190                Debug.Assert(value == null);
 0191            }
 192
 0193            if (IgnoreDefaultValuesOnWrite)
 0194            {
 195                // Fast path `ShouldSerialize` check when using JsonIgnoreCondition.WhenWritingNull/Default configuratio
 0196                if (IsDefaultValue(value))
 0197                {
 0198                    return true;
 199                }
 0200            }
 0201            else if (ShouldSerialize?.Invoke(obj, value) == false)
 0202            {
 203                // We return true here.
 204                // False means that there is not enough data.
 0205                return true;
 206            }
 207
 0208            if (value is null)
 0209            {
 0210                Debug.Assert(PropertyTypeCanBeNull);
 211
 0212                if (!IsGetNullable && Options.RespectNullableAnnotations)
 0213                {
 0214                    ThrowHelper.ThrowJsonException_PropertyGetterDisallowNull(Name, state.Current.JsonTypeInfo.Type);
 215                }
 216
 0217                if (EffectiveConverter.HandleNullOnWrite)
 0218                {
 0219                    if (state.Current.PropertyState < StackFramePropertyState.Name)
 0220                    {
 0221                        state.Current.PropertyState = StackFramePropertyState.Name;
 0222                        writer.WritePropertyNameSection(EscapedNameSection);
 0223                    }
 224
 0225                    int originalDepth = writer.CurrentDepth;
 0226                    EffectiveConverter.Write(writer, value, Options);
 0227                    if (originalDepth != writer.CurrentDepth)
 0228                    {
 0229                        ThrowHelper.ThrowJsonException_SerializationConverterWrite(EffectiveConverter);
 230                    }
 0231                }
 232                else
 0233                {
 0234                    writer.WriteNullSection(EscapedNameSection);
 0235                }
 236
 0237                return true;
 238            }
 239            else
 0240            {
 0241                if (state.Current.PropertyState < StackFramePropertyState.Name)
 0242                {
 0243                    state.Current.PropertyState = StackFramePropertyState.Name;
 0244                    writer.WritePropertyNameSection(EscapedNameSection);
 0245                }
 246
 0247                return EffectiveConverter.TryWrite(writer, value, Options, ref state);
 248            }
 0249        }
 250
 251        internal override bool GetMemberAndWriteJsonExtensionData(object obj, ref WriteStack state, Utf8JsonWriter write
 0252        {
 253            bool success;
 0254            T value = Get!(obj);
 255
 0256            if (ShouldSerialize?.Invoke(obj, value) == false)
 0257            {
 258                // We return true here.
 259                // False means that there is not enough data.
 0260                return true;
 261            }
 262
 0263            if (value == null)
 0264            {
 0265                success = true;
 0266            }
 267            else
 0268            {
 0269                success = EffectiveConverter.TryWriteDataExtensionProperty(writer, value, Options, ref state);
 0270            }
 271
 0272            return success;
 0273        }
 274
 275        internal override bool ReadJsonAndSetMember(object obj, scoped ref ReadStack state, ref Utf8JsonReader reader)
 0276        {
 277            bool success;
 278
 0279            bool isNullToken = reader.TokenType == JsonTokenType.Null;
 280
 0281            if (isNullToken && !EffectiveConverter.HandleNullOnRead && !state.IsContinuation)
 0282            {
 0283                if (default(T) is not null || !CanDeserialize)
 0284                {
 0285                    if (default(T) is null)
 0286                    {
 0287                        Debug.Assert(CanDeserialize || EffectiveObjectCreationHandling == JsonObjectCreationHandling.Pop
 0288                        ThrowHelper.ThrowInvalidOperationException_DeserializeUnableToAssignNull(EffectiveConverter.Type
 289                    }
 290
 0291                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(EffectiveConverter.Type);
 292                }
 293
 0294                if (!IgnoreNullTokensOnRead)
 0295                {
 0296                    if (!IsSetNullable && Options.RespectNullableAnnotations)
 0297                    {
 0298                        ThrowHelper.ThrowJsonException_PropertySetterDisallowNull(Name, state.Current.JsonTypeInfo.Type)
 299                    }
 300
 0301                    T? value = default;
 0302                    Set!(obj, value!);
 0303                }
 304
 0305                success = true;
 0306                state.Current.MarkPropertyAsRead(this);
 0307            }
 0308            else if (EffectiveConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
 0309            {
 310                // CanUseDirectReadOrWrite == false when using streams
 0311                Debug.Assert(!state.IsContinuation);
 0312                Debug.Assert(EffectiveObjectCreationHandling != JsonObjectCreationHandling.Populate, "Populating should 
 313
 0314                if (!isNullToken || !IgnoreNullTokensOnRead || default(T) is not null)
 0315                {
 316                    // Optimize for internal converters by avoiding the extra call to TryRead.
 0317                    T? fastValue = EffectiveConverter.Read(ref reader, PropertyType, Options);
 318
 0319                    if (fastValue is null && !IsSetNullable && Options.RespectNullableAnnotations)
 0320                    {
 0321                        ThrowHelper.ThrowJsonException_PropertySetterDisallowNull(Name, state.Current.JsonTypeInfo.Type)
 322                    }
 323
 0324                    Set!(obj, fastValue!);
 0325                }
 326
 0327                success = true;
 0328                state.Current.MarkPropertyAsRead(this);
 0329            }
 330            else
 0331            {
 0332                success = true;
 0333                if (!isNullToken || !IgnoreNullTokensOnRead || default(T) is not null || state.IsContinuation)
 0334                {
 0335                    state.Current.ReturnValue = obj;
 336
 0337                    success = EffectiveConverter.TryRead(ref reader, PropertyType, Options, ref state, out T? value, out
 0338                    if (success)
 0339                    {
 0340                        if (typeof(T).IsValueType || !populatedValue)
 0341                        {
 342                            // note: populatedValue value may be different than when CreationHandling is Populate
 343                            //       i.e. when initial value of property is null
 344
 345                            // We cannot do reader.Skip early because converter decides if populating will happen or not
 0346                            if (CanDeserialize)
 0347                            {
 0348                                if (value is null && !IsSetNullable && Options.RespectNullableAnnotations)
 0349                                {
 0350                                    ThrowHelper.ThrowJsonException_PropertySetterDisallowNull(Name, state.Current.JsonTy
 351                                }
 352
 0353                                Set!(obj, value!);
 0354                            }
 0355                        }
 356
 0357                        state.Current.MarkPropertyAsRead(this);
 0358                    }
 0359                }
 0360            }
 361
 0362            return success;
 0363        }
 364
 365        internal override bool ReadJsonAsObject(scoped ref ReadStack state, ref Utf8JsonReader reader, out object? value
 0366        {
 367            bool success;
 0368            bool isNullToken = reader.TokenType == JsonTokenType.Null;
 0369            if (isNullToken && !EffectiveConverter.HandleNullOnRead && !state.IsContinuation)
 0370            {
 0371                if (default(T) is not null)
 0372                {
 0373                    ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(EffectiveConverter.Type);
 374                }
 375
 0376                value = default(T);
 0377                success = true;
 0378            }
 379            else
 0380            {
 381                // Optimize for internal converters by avoiding the extra call to TryRead.
 0382                if (EffectiveConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
 0383                {
 384                    // CanUseDirectReadOrWrite == false when using streams
 0385                    Debug.Assert(!state.IsContinuation);
 386
 0387                    value = EffectiveConverter.Read(ref reader, PropertyType, Options);
 0388                    success = true;
 0389                }
 390                else
 0391                {
 0392                    success = EffectiveConverter.TryRead(ref reader, PropertyType, Options, ref state, out T? typedValue
 0393                    value = typedValue;
 0394                }
 0395            }
 396
 0397            return success;
 0398        }
 399
 400        private protected override void ConfigureIgnoreCondition(JsonIgnoreCondition? ignoreCondition)
 7794401        {
 7794402            switch (ignoreCondition)
 403            {
 404                case null:
 7794405                    break;
 406
 407                case JsonIgnoreCondition.Never:
 0408                    ShouldSerialize = ShouldSerializeIgnoreConditionNever;
 0409                    break;
 410
 411                case JsonIgnoreCondition.Always:
 0412                    ShouldSerialize = ShouldSerializeIgnoreConditionAlways;
 0413                    break;
 414
 415                case JsonIgnoreCondition.WhenWritingNull:
 0416                    if (PropertyTypeCanBeNull)
 0417                    {
 0418                        ShouldSerialize = ShouldSerializeIgnoreWhenWritingDefault;
 0419                        IgnoreDefaultValuesOnWrite = true;
 0420                    }
 421                    else
 0422                    {
 0423                        ThrowHelper.ThrowInvalidOperationException_IgnoreConditionOnValueTypeInvalid(MemberName!, Declar
 424                    }
 0425                    break;
 426
 427                case JsonIgnoreCondition.WhenWritingDefault:
 0428                    ShouldSerialize = ShouldSerializeIgnoreWhenWritingDefault;
 0429                    IgnoreDefaultValuesOnWrite = true;
 0430                    break;
 431
 432                case JsonIgnoreCondition.WhenWriting:
 0433                    ShouldSerialize = ShouldSerializeIgnoreConditionAlways;
 0434                    break;
 435
 436                case JsonIgnoreCondition.WhenReading:
 0437                    Set = null;
 0438                    break;
 439
 440                default:
 0441                    Debug.Fail($"Unknown value of JsonIgnoreCondition '{ignoreCondition}'");
 442                    break;
 443            }
 444
 0445            static bool ShouldSerializeIgnoreConditionNever(object _, T? value) => true;
 0446            static bool ShouldSerializeIgnoreConditionAlways(object _, T? value) => false;
 447            static bool ShouldSerializeIgnoreWhenWritingDefault(object _, T? value)
 0448            {
 0449                return default(T) is null ? value is not null : !EqualityComparer<T>.Default.Equals(default, value);
 0450            }
 7794451        }
 452
 453        private static bool IsDefaultValue(T? value)
 0454        {
 0455            return default(T) is null ? value is null : EqualityComparer<T>.Default.Equals(default, value);
 0456        }
 457    }
 458}