| | | 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 | | |
| | | 4 | | using System.Diagnostics; |
| | | 5 | | using System.Diagnostics.CodeAnalysis; |
| | | 6 | | using System.Text.Json.Nodes; |
| | | 7 | | using System.Text.Json.Reflection; |
| | | 8 | | using System.Text.Json.Schema; |
| | | 9 | | |
| | | 10 | | namespace System.Text.Json.Serialization.Converters |
| | | 11 | | { |
| | | 12 | | /// <summary> |
| | | 13 | | /// Converter wrapper which casts SourceType into TargetType |
| | | 14 | | /// </summary> |
| | | 15 | | internal sealed class CastingConverter<T> : JsonConverter<T> |
| | | 16 | | { |
| | | 17 | | private readonly JsonConverter _sourceConverter; |
| | 0 | 18 | | internal override Type? KeyType => _sourceConverter.KeyType; |
| | 0 | 19 | | internal override Type? ElementType => _sourceConverter.ElementType; |
| | 0 | 20 | | internal override JsonConverter? NullableElementConverter => _sourceConverter.NullableElementConverter; |
| | | 21 | | |
| | 0 | 22 | | public override bool HandleNull { get; } |
| | 0 | 23 | | internal override bool SupportsCreateObjectDelegate => _sourceConverter.SupportsCreateObjectDelegate; |
| | | 24 | | |
| | 0 | 25 | | internal CastingConverter(JsonConverter sourceConverter) |
| | 0 | 26 | | { |
| | 0 | 27 | | Debug.Assert(typeof(T).IsInSubtypeRelationshipWith(sourceConverter.Type!)); |
| | 0 | 28 | | Debug.Assert(sourceConverter.SourceConverterForCastingConverter is null, "casting converters should not be l |
| | | 29 | | |
| | 0 | 30 | | _sourceConverter = sourceConverter; |
| | 0 | 31 | | IsInternalConverter = sourceConverter.IsInternalConverter; |
| | 0 | 32 | | IsInternalConverterForNumberType = sourceConverter.IsInternalConverterForNumberType; |
| | 0 | 33 | | ConverterStrategy = sourceConverter.ConverterStrategy; |
| | 0 | 34 | | CanBePolymorphic = sourceConverter.CanBePolymorphic; |
| | | 35 | | |
| | | 36 | | // Ensure HandleNull values reflect the exact configuration of the source converter |
| | 0 | 37 | | HandleNullOnRead = sourceConverter.HandleNullOnRead; |
| | 0 | 38 | | HandleNullOnWrite = sourceConverter.HandleNullOnWrite; |
| | 0 | 39 | | HandleNull = sourceConverter.HandleNullOnWrite; |
| | 0 | 40 | | } |
| | | 41 | | |
| | 0 | 42 | | internal override JsonConverter? SourceConverterForCastingConverter => _sourceConverter; |
| | | 43 | | |
| | | 44 | | public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) |
| | 0 | 45 | | => JsonSerializer.UnboxOnRead<T>(_sourceConverter.ReadAsObject(ref reader, typeToConvert, options)); |
| | | 46 | | |
| | | 47 | | public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) |
| | 0 | 48 | | => _sourceConverter.WriteAsObject(writer, value, options); |
| | | 49 | | |
| | | 50 | | internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, s |
| | 0 | 51 | | { |
| | 0 | 52 | | bool result = _sourceConverter.OnTryReadAsObject(ref reader, typeToConvert, options, ref state, out object? |
| | 0 | 53 | | value = JsonSerializer.UnboxOnRead<T>(sourceValue); |
| | 0 | 54 | | return result; |
| | 0 | 55 | | } |
| | | 56 | | |
| | | 57 | | internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack |
| | 0 | 58 | | => _sourceConverter.OnTryWriteAsObject(writer, value, options, ref state); |
| | | 59 | | |
| | | 60 | | public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions option |
| | 0 | 61 | | { |
| | 0 | 62 | | if (_sourceConverter.Type == typeof(T)) |
| | 0 | 63 | | { |
| | 0 | 64 | | return JsonSerializer.UnboxOnRead<T>(_sourceConverter.ReadAsPropertyNameAsObject(ref reader, typeToConve |
| | | 65 | | } |
| | | 66 | | else |
| | 0 | 67 | | { |
| | | 68 | | // The source converter's Type doesn't match T. Using the source converter's |
| | | 69 | | // ReadAsPropertyName could fail or behave incorrectly: the source's fallback |
| | | 70 | | // would look up a converter for its type (e.g. object), not T. Use our base |
| | | 71 | | // class logic instead, which correctly uses the fallback for type T. |
| | 0 | 72 | | return base.ReadAsPropertyName(ref reader, typeToConvert, options); |
| | | 73 | | } |
| | 0 | 74 | | } |
| | | 75 | | |
| | | 76 | | internal override T ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions |
| | 0 | 77 | | { |
| | 0 | 78 | | if (_sourceConverter.Type == typeof(T)) |
| | 0 | 79 | | { |
| | 0 | 80 | | return JsonSerializer.UnboxOnRead<T>(_sourceConverter.ReadAsPropertyNameCoreAsObject(ref reader, typeToC |
| | | 81 | | } |
| | | 82 | | else |
| | 0 | 83 | | { |
| | | 84 | | // The source converter's Type doesn't match T. Using the source converter's |
| | | 85 | | // ReadAsPropertyNameCore could fail or behave incorrectly: the source's fallback |
| | | 86 | | // would look up a converter for its type (e.g. object), not T. Use our base |
| | | 87 | | // class logic instead, which correctly uses the fallback for type T. |
| | 0 | 88 | | return base.ReadAsPropertyNameCore(ref reader, typeToConvert, options); |
| | | 89 | | } |
| | 0 | 90 | | } |
| | | 91 | | |
| | | 92 | | public override void WriteAsPropertyName(Utf8JsonWriter writer, [DisallowNull] T value, JsonSerializerOptions op |
| | 0 | 93 | | { |
| | 0 | 94 | | if (_sourceConverter.Type == typeof(T)) |
| | 0 | 95 | | { |
| | 0 | 96 | | _sourceConverter.WriteAsPropertyNameAsObject(writer, value, options); |
| | 0 | 97 | | } |
| | | 98 | | else |
| | 0 | 99 | | { |
| | | 100 | | // The source converter's Type doesn't match T. Using the source converter's |
| | | 101 | | // WriteAsPropertyName could cause infinite recursion: the source's fallback |
| | | 102 | | // would look up a converter for its type (e.g. object), which might delegate |
| | | 103 | | // back to us via the runtime type lookup. Use our base class logic instead, |
| | | 104 | | // which correctly uses the fallback for type T. |
| | 0 | 105 | | base.WriteAsPropertyName(writer, value, options); |
| | 0 | 106 | | } |
| | 0 | 107 | | } |
| | | 108 | | |
| | | 109 | | internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, T value, JsonSerializerOptions options, bo |
| | 0 | 110 | | { |
| | 0 | 111 | | if (_sourceConverter.Type == typeof(T)) |
| | 0 | 112 | | { |
| | 0 | 113 | | _sourceConverter.WriteAsPropertyNameCoreAsObject(writer, value, options, isWritingExtensionDataProperty) |
| | 0 | 114 | | } |
| | | 115 | | else |
| | 0 | 116 | | { |
| | | 117 | | // The source converter's Type doesn't match T. Using the source converter's |
| | | 118 | | // WriteAsPropertyNameCore could cause infinite recursion: the source's fallback |
| | | 119 | | // would look up a converter for its type (e.g. object), which might delegate |
| | | 120 | | // back to us via the runtime type lookup. Use our base class logic instead, |
| | | 121 | | // which correctly uses the fallback for type T. |
| | 0 | 122 | | base.WriteAsPropertyNameCore(writer, value!, options, isWritingExtensionDataProperty); |
| | 0 | 123 | | } |
| | 0 | 124 | | } |
| | | 125 | | |
| | | 126 | | internal override T ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSer |
| | 0 | 127 | | => JsonSerializer.UnboxOnRead<T>(_sourceConverter.ReadNumberWithCustomHandlingAsObject(ref reader, handling, |
| | | 128 | | |
| | | 129 | | internal override void WriteNumberWithCustomHandling(Utf8JsonWriter writer, T? value, JsonNumberHandling handlin |
| | 0 | 130 | | => _sourceConverter.WriteNumberWithCustomHandlingAsObject(writer, value, handling); |
| | | 131 | | |
| | | 132 | | internal override JsonSchema? GetSchema(JsonNumberHandling numberHandling) |
| | 0 | 133 | | => _sourceConverter.GetSchema(numberHandling); |
| | | 134 | | } |
| | | 135 | | } |