< Summary

Line coverage
20%
Covered lines: 35
Uncovered lines: 136
Coverable lines: 171
Total lines: 465
Line coverage: 20.4%
Branch coverage
4%
Covered branches: 3
Total branches: 66
Branch coverage: 4.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.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.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.Reflection;
 7using System.Text.Json.Schema;
 8using System.Text.Json.Serialization.Converters;
 9using System.Text.Json.Serialization.Metadata;
 10
 11namespace System.Text.Json.Serialization
 12{
 13    /// <summary>
 14    /// Converts an object or value to or from JSON.
 15    /// </summary>
 16    public abstract partial class JsonConverter
 17    {
 127218        internal JsonConverter()
 127219        {
 127220            IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
 127221            ConverterStrategy = GetDefaultConverterStrategy();
 127222        }
 23
 24        /// <summary>
 25        /// Gets the type being converted by the current converter instance.
 26        /// </summary>
 27        /// <remarks>
 28        /// For instances of type <see cref="JsonConverter{T}"/> returns typeof(T),
 29        /// and for instances of type <see cref="JsonConverterFactory"/> returns <see langword="null" />.
 30        /// </remarks>
 31        public abstract Type? Type { get; }
 32
 33        /// <summary>
 34        /// Determines whether the type can be converted.
 35        /// </summary>
 36        /// <param name="typeToConvert">The type is checked as to whether it can be converted.</param>
 37        /// <returns>True if the type can be converted, false otherwise.</returns>
 38        public abstract bool CanConvert(Type typeToConvert);
 39
 40        internal ConverterStrategy ConverterStrategy
 41        {
 1581042            get;
 43            init
 127244            {
 127245                CanUseDirectReadOrWrite = value == ConverterStrategy.Value && IsInternalConverter;
 127246                RequiresReadAhead = value == ConverterStrategy.Value;
 127247                field = value;
 127248            }
 49        }
 50
 51        /// <summary>
 52        /// Invoked by the base contructor to populate the initial value of the <see cref="ConverterStrategy"/> property
 53        /// Used for declaring the default strategy for specific converter hierarchies without explicitly setting in a c
 54        /// </summary>
 55        private protected abstract ConverterStrategy GetDefaultConverterStrategy();
 56
 57        /// <summary>
 58        /// Indicates that the converter can consume the <see cref="JsonTypeInfo.CreateObject"/> delegate.
 59        /// Needed because certain collection converters cannot support arbitrary delegates.
 60        /// TODO remove once https://github.com/dotnet/runtime/pull/73395/ and
 61        /// https://github.com/dotnet/runtime/issues/71944 have been addressed.
 62        /// </summary>
 318263        internal virtual bool SupportsCreateObjectDelegate => false;
 64
 65        /// <summary>
 66        /// Indicates that the converter is compatible with <see cref="JsonObjectCreationHandling.Populate"/>.
 67        /// </summary>
 068        internal virtual bool CanPopulate => false;
 69
 70        /// <summary>
 71        /// Can direct Read or Write methods be called (for performance).
 72        /// </summary>
 127273        internal bool CanUseDirectReadOrWrite { get; set; }
 74
 75        /// <summary>
 76        /// The converter supports writing and reading metadata.
 77        /// </summary>
 078        internal virtual bool CanHaveMetadata => false;
 79
 80        /// <summary>
 81        /// The converter supports polymorphic writes; only reserved for System.Object types.
 82        /// </summary>
 13783        internal bool CanBePolymorphic { get; init; }
 84
 85        /// <summary>
 86        /// The converter can read/write multiple JSON value shapes (e.g. both strings and objects).
 87        /// Relaxes debug validation that non-value converters must always read StartObject/StartArray tokens.
 88        /// </summary>
 089        internal bool SupportsMultipleTokenTypes { get; init; }
 90
 91        /// <summary>
 92        /// The serializer must read ahead all contents of the next JSON value
 93        /// before calling into the converter for deserialization.
 94        /// </summary>
 2449395        internal bool RequiresReadAhead { get; private protected set; }
 96
 97        /// <summary>
 98        /// Whether the converter is a special root-level value streaming converter.
 99        /// </summary>
 23206100        internal bool IsRootLevelMultiContentStreamingConverter { get; init; }
 101
 102        /// <summary>
 103        /// Used to support JsonObject as an extension property in a loosely-typed, trimmable manner.
 104        /// </summary>
 105        internal virtual void ReadElementAndSetProperty(
 106            object obj,
 107            string propertyName,
 108            ref Utf8JsonReader reader,
 109            JsonSerializerOptions options,
 110            scoped ref ReadStack state)
 0111        {
 0112            Debug.Fail("Should not be reachable.");
 113
 114            throw new InvalidOperationException();
 115        }
 116
 117        internal virtual JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions options)
 0118        {
 0119            Debug.Fail("Should not be reachable.");
 120
 121            throw new InvalidOperationException();
 122        }
 123
 124        internal JsonConverter<TTarget> CreateCastingConverter<TTarget>()
 4147125        {
 4147126            Debug.Assert(this is not JsonConverterFactory);
 127
 4147128            if (this is JsonConverter<TTarget> conv)
 4147129            {
 4147130                return conv;
 131            }
 132            else
 0133            {
 0134                JsonSerializerOptions.CheckConverterNullabilityIsSameAsPropertyType(this, typeof(TTarget));
 135
 136                // Avoid layering casting converters by consulting any source converters directly.
 0137                return
 0138                    SourceConverterForCastingConverter?.CreateCastingConverter<TTarget>()
 0139                    ?? new CastingConverter<TTarget>(this);
 140            }
 4147141        }
 142
 143        /// <summary>
 144        /// Tracks whether the JsonConverter&lt;T&gt;.HandleNull property has been overridden by a derived converter.
 145        /// </summary>
 1381146        internal bool UsesDefaultHandleNull { get; private protected set; }
 147
 148        /// <summary>
 149        /// Does the converter want to be called when reading null tokens.
 150        /// When JsonConverter&lt;T&gt;.HandleNull isn't overridden this can still be true for non-nullable structs.
 151        /// </summary>
 211152        internal bool HandleNullOnRead { get; private protected init; }
 153
 154        /// <summary>
 155        /// Does the converter want to be called for null values.
 156        /// Should always match the precise value of the JsonConverter&lt;T&gt;.HandleNull virtual property.
 157        /// </summary>
 211158        internal bool HandleNullOnWrite { get; private protected init; }
 159
 160        /// <summary>
 161        /// Set if this converter is itself a casting converter.
 162        /// </summary>
 0163        internal virtual JsonConverter? SourceConverterForCastingConverter => null;
 164
 3678165        internal virtual Type? ElementType => null;
 166
 3876167        internal virtual Type? KeyType => null;
 168
 496169        internal virtual JsonConverter? NullableElementConverter => null;
 170
 171        /// <summary>
 172        /// Cached value of TypeToConvert.IsValueType, which is an expensive call.
 173        /// </summary>
 3498174        internal bool IsValueType { get; init; }
 175
 176        /// <summary>
 177        /// Indicates whether this converter handles IEEE 754 floating-point types
 178        /// (double, float, Half) that may emit anyOf schemas with named floating-point
 179        /// literals under AllowNamedFloatingPointLiterals.
 180        /// </summary>
 0181        internal virtual bool IsIeeeFloatingPointConverter => false;
 182
 183        /// <summary>
 184        /// Whether the converter is built-in.
 185        /// </summary>
 9749186        internal bool IsInternalConverter { get; init; }
 187
 188        /// <summary>
 189        /// Whether the converter is built-in and handles a number type.
 190        /// </summary>
 11344191        internal bool IsInternalConverterForNumberType { get; init; }
 192
 193        /// <summary>
 194        /// Whether the converter handles collection deserialization by converting from
 195        /// an intermediate buffer such as immutable collections, arrays or memory types.
 196        /// Used in conjunction with <see cref="JsonCollectionConverter{TCollection, TElement}.ConvertCollection(ref Rea
 197        /// </summary>
 0198        internal virtual bool IsConvertibleCollection => false;
 199
 200        internal static bool ShouldFlush(ref WriteStack state, Utf8JsonWriter writer)
 0201        {
 0202            Debug.Assert(state.FlushThreshold == 0 || (state.PipeWriter is { CanGetUnflushedBytes: true }),
 0203                "ShouldFlush should only be called by resumable serializers, all of which use the PipeWriter abstraction
 204            // If surpassed flush threshold then return true which will flush stream.
 0205            if (state.PipeWriter is { } pipeWriter)
 0206            {
 0207                return state.FlushThreshold > 0 && pipeWriter.UnflushedBytes > state.FlushThreshold - writer.BytesPendin
 208            }
 209
 0210            return false;
 0211        }
 212
 213        internal abstract object? ReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions opti
 214        internal abstract bool OnTryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions op
 215        internal abstract bool TryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions opti
 216        internal abstract object? ReadAsPropertyNameAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializ
 217        internal abstract object? ReadAsPropertyNameCoreAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSeri
 218        internal abstract object? ReadNumberWithCustomHandlingAsObject(ref Utf8JsonReader reader, JsonNumberHandling han
 219
 220        internal abstract void WriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options);
 221        internal abstract bool OnTryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, r
 222        internal abstract bool TryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref
 223        internal abstract void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions o
 224        internal abstract void WriteAsPropertyNameCoreAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptio
 225        internal abstract void WriteNumberWithCustomHandlingAsObject(Utf8JsonWriter writer, object? value, JsonNumberHan
 226
 227        /// <summary>
 228        /// Gets a schema from the type being converted
 229        /// </summary>
 0230        internal virtual JsonSchema? GetSchema(JsonNumberHandling numberHandling) => null;
 231
 232        /// <summary>
 233        /// Gets the set of leading JSON value shapes that this converter can read
 234        /// for the given <paramref name="numberHandling"/>. The base implementation returns
 235        /// <see cref="JsonValueType.Any"/>. Used by union-type dispatch to build a
 236        /// value-shape-to-case map without re-walking type metadata. Mirrors <see cref="GetSchema"/>.
 237        /// </summary>
 238        /// <remarks>
 239        /// When adding or changing an override on a built-in converter (under
 240        /// <c>Serialization/Converters/Value/</c>), also update
 241        /// <c>gen/JsonSourceGenerator.Parser.cs::GetSupportedJsonValueTypes</c> so the source
 242        /// generator's union ambiguity diagnostic (SYSLIB1227) agrees with the runtime
 243        /// union value-shape map.
 244        /// </remarks>
 0245        internal virtual JsonValueType GetSupportedJsonValueTypes(JsonNumberHandling numberHandling) => JsonValueType.An
 246
 247        // Whether a type (ConverterStrategy.Object) is deserialized using a parameterized constructor.
 5913248        internal virtual bool ConstructorIsParameterized { get; }
 249
 6797250        internal ConstructorInfo? ConstructorInfo { get; set; }
 251
 252        /// <summary>
 253        /// Used for hooking custom configuration to a newly created associated JsonTypeInfo instance.
 254        /// </summary>
 8122255        internal virtual void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { }
 256
 257        /// <summary>
 258        /// Additional reflection-specific configuration required by certain collection converters.
 259        /// </summary>
 260        [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
 261        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
 7628262        internal virtual void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions opti
 263    }
 264}
 265

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.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.Diagnostics;
 5using System.Text.Json.Serialization.Metadata;
 6
 7namespace System.Text.Json.Serialization
 8{
 9    public partial class JsonConverter
 10    {
 11        /// <summary>
 12        /// Initializes the state for polymorphic cases and returns the appropriate derived converter.
 13        /// </summary>
 14        internal JsonConverter? ResolvePolymorphicConverter(JsonTypeInfo jsonTypeInfo, ref ReadStack state)
 015        {
 016            Debug.Assert(!IsValueType);
 017            Debug.Assert(CanHaveMetadata);
 018            Debug.Assert((state.Current.MetadataPropertyNames & MetadataPropertyName.Type) != 0 || state.PolymorphicReso
 019            Debug.Assert(state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntry
 20
 021            JsonConverter? polymorphicConverter = null;
 22
 023            switch (state.Current.PolymorphicSerializationState)
 24            {
 25                case PolymorphicSerializationState.None:
 026                    Debug.Assert(!state.IsContinuation);
 27
 028                    PolymorphicTypeResolver resolver = jsonTypeInfo.PolymorphicTypeResolver!;
 29                    JsonTypeInfo? resolvedType;
 30
 031                    if (state.PolymorphicResolvedType is Type classifiedType)
 032                    {
 33                        // Type resolved by custom TypeClassifier â€” resolve directly.
 034                        resolver.TryResolveDerivedJsonTypeInfo(classifiedType, out resolvedType);
 035                        state.PolymorphicResolvedType = null;
 036                    }
 37                    else
 038                    {
 39                        // Standard discriminator-based resolution.
 040                        Debug.Assert(state.PolymorphicTypeDiscriminator is not null);
 041                        Debug.Assert(resolver.UsesTypeDiscriminators);
 042                        resolver.TryGetDerivedJsonTypeInfo(state.PolymorphicTypeDiscriminator, out resolvedType);
 043                    }
 44
 045                    if (resolvedType is not null)
 046                    {
 047                        Debug.Assert(Type!.IsAssignableFrom(resolvedType.Type));
 48
 049                        polymorphicConverter = state.InitializePolymorphicReEntry(resolvedType);
 050                        if (!polymorphicConverter.CanHaveMetadata)
 051                        {
 052                            ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(resolvedType.T
 53                        }
 054                    }
 55                    else
 056                    {
 057                        state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNo
 058                    }
 59
 060                    state.PolymorphicTypeDiscriminator = null;
 061                    break;
 62
 63                case PolymorphicSerializationState.PolymorphicReEntrySuspended:
 064                    polymorphicConverter = state.ResumePolymorphicReEntry();
 065                    Debug.Assert(Type!.IsAssignableFrom(polymorphicConverter.Type));
 066                    break;
 67
 68                case PolymorphicSerializationState.PolymorphicReEntryNotFound:
 069                    Debug.Assert(state.Current.PolymorphicJsonTypeInfo is null);
 070                    break;
 71
 72                default:
 073                    Debug.Fail("Unexpected PolymorphicSerializationState.");
 74                    break;
 75            }
 76
 077            return polymorphicConverter;
 078        }
 79
 80        /// <summary>
 81        /// Initializes the state for polymorphic cases and returns the appropriate derived converter.
 82        /// </summary>
 83        internal JsonConverter? ResolvePolymorphicConverter(object value, JsonTypeInfo jsonTypeInfo, JsonSerializerOptio
 084        {
 085            Debug.Assert(!IsValueType);
 086            Debug.Assert(value is not null && Type!.IsAssignableFrom(value.GetType()));
 087            Debug.Assert(CanBePolymorphic || jsonTypeInfo.PolymorphicTypeResolver is not null);
 088            Debug.Assert(state.PolymorphicTypeDiscriminator is null);
 89
 090            JsonConverter? polymorphicConverter = null;
 91
 092            switch (state.Current.PolymorphicSerializationState)
 93            {
 94                case PolymorphicSerializationState.None:
 095                    Debug.Assert(!state.IsContinuation);
 96
 097                    Type runtimeType = value.GetType();
 98
 099                    if (CanBePolymorphic && runtimeType != Type)
 0100                    {
 0101                        Debug.Assert(Type == typeof(object));
 0102                        jsonTypeInfo = state.Current.InitializePolymorphicReEntry(runtimeType, options);
 0103                        polymorphicConverter = jsonTypeInfo.Converter;
 0104                    }
 105
 0106                    if (jsonTypeInfo.PolymorphicTypeResolver is PolymorphicTypeResolver resolver)
 0107                    {
 0108                        Debug.Assert(jsonTypeInfo.Converter.CanHaveMetadata);
 109
 0110                        if (resolver.TryGetDerivedJsonTypeInfo(runtimeType, out JsonTypeInfo? derivedJsonTypeInfo, out o
 0111                        {
 0112                            polymorphicConverter = state.Current.InitializePolymorphicReEntry(derivedJsonTypeInfo);
 113
 0114                            if (typeDiscriminator is not null)
 0115                            {
 0116                                if (!polymorphicConverter.CanHaveMetadata)
 0117                                {
 0118                                    ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(derive
 119                                }
 120
 0121                                state.PolymorphicTypeDiscriminator = typeDiscriminator;
 0122                                state.PolymorphicTypeResolver = resolver;
 0123                            }
 0124                        }
 0125                    }
 126
 0127                    if (polymorphicConverter is null)
 0128                    {
 0129                        state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNo
 0130                    }
 131
 0132                    break;
 133
 134                case PolymorphicSerializationState.PolymorphicReEntrySuspended:
 0135                    Debug.Assert(state.IsContinuation);
 0136                    polymorphicConverter = state.Current.ResumePolymorphicReEntry();
 0137                    Debug.Assert(Type.IsAssignableFrom(polymorphicConverter.Type));
 0138                    break;
 139
 140                case PolymorphicSerializationState.PolymorphicReEntryNotFound:
 0141                    Debug.Assert(state.IsContinuation);
 0142                    break;
 143
 144                default:
 0145                    Debug.Fail("Unexpected PolymorphicSerializationState.");
 146                    break;
 147            }
 148
 0149            return polymorphicConverter;
 0150        }
 151
 152        internal bool TryHandleSerializedObjectReference(Utf8JsonWriter writer, object value, JsonSerializerOptions opti
 0153        {
 0154            Debug.Assert(!IsValueType);
 0155            Debug.Assert(!state.IsContinuation);
 0156            Debug.Assert(value is not null);
 157
 0158            switch (options.ReferenceHandlingStrategy)
 159            {
 160                case JsonKnownReferenceHandler.IgnoreCycles:
 0161                    ReferenceResolver resolver = state.ReferenceResolver;
 0162                    if (resolver.ContainsReferenceForCycleDetection(value))
 0163                    {
 0164                        writer.WriteNullValue();
 165
 0166                        if (polymorphicConverter is not null)
 0167                        {
 168                            // Clear out any polymorphic state.
 0169                            state.PolymorphicTypeDiscriminator = null;
 0170                            state.PolymorphicTypeResolver = null;
 0171                        }
 0172                        return true;
 173                    }
 174
 0175                    resolver.PushReferenceForCycleDetection(value);
 176                    // WriteStack reuses root-level stack frames for its children as a performance optimization;
 177                    // we want to avoid writing any data for the root-level object to avoid corrupting the stack.
 178                    // This is fine since popping the root object at the end of serialization is not essential.
 0179                    state.Current.IsPushedReferenceForCycleDetection = state.CurrentDepth > 0;
 0180                    break;
 181
 182                case JsonKnownReferenceHandler.Preserve:
 0183                    bool canHaveIdMetadata = polymorphicConverter?.CanHaveMetadata ?? CanHaveMetadata;
 0184                    if (canHaveIdMetadata && JsonSerializer.TryGetReferenceForValue(value, ref state, writer))
 0185                    {
 186                        // We found a repeating reference and wrote the relevant metadata; serialization complete.
 0187                        return true;
 188                    }
 0189                    break;
 190
 191                default:
 0192                    Debug.Fail("Unexpected ReferenceHandlingStrategy.");
 193                    break;
 194            }
 195
 0196            return false;
 0197        }
 198    }
 199}
 200

Methods/Properties

.ctor()
ConverterStrategy()
ConverterStrategy(System.Text.Json.ConverterStrategy)
SupportsCreateObjectDelegate()
CanPopulate()
CanUseDirectReadOrWrite()
CanHaveMetadata()
CanBePolymorphic()
SupportsMultipleTokenTypes()
RequiresReadAhead()
IsRootLevelMultiContentStreamingConverter()
ReadElementAndSetProperty(System.Object,System.String,System.Text.Json.Utf8JsonReader&,System.Text.Json.JsonSerializerOptions,System.Text.Json.ReadStack&)
CreateJsonTypeInfo(System.Text.Json.JsonSerializerOptions)
CreateCastingConverter()
UsesDefaultHandleNull()
HandleNullOnRead()
HandleNullOnWrite()
SourceConverterForCastingConverter()
ElementType()
KeyType()
NullableElementConverter()
IsValueType()
IsIeeeFloatingPointConverter()
IsInternalConverter()
IsInternalConverterForNumberType()
IsConvertibleCollection()
ShouldFlush(System.Text.Json.WriteStack&,System.Text.Json.Utf8JsonWriter)
GetSchema(System.Text.Json.Serialization.JsonNumberHandling)
GetSupportedJsonValueTypes(System.Text.Json.Serialization.JsonNumberHandling)
ConstructorIsParameterized()
ConstructorInfo()
ConfigureJsonTypeInfo(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Text.Json.JsonSerializerOptions)
ConfigureJsonTypeInfoUsingReflection(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Text.Json.JsonSerializerOptions)
ResolvePolymorphicConverter(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Text.Json.ReadStack&)
ResolvePolymorphicConverter(System.Object,System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Text.Json.JsonSerializerOptions,System.Text.Json.WriteStack&)
TryHandleSerializedObjectReference(System.Text.Json.Utf8JsonWriter,System.Object,System.Text.Json.JsonSerializerOptions,System.Text.Json.Serialization.JsonConverter,System.Text.Json.WriteStack&)