diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs
index fb3dad041fc70..9318301476a5e 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Runtime.CompilerServices;
using System.Text.Json.Reflection;
namespace System.Text.Json.Serialization.Converters
@@ -9,18 +10,18 @@ namespace System.Text.Json.Serialization.Converters
///
/// Converter wrapper which casts SourceType into TargetType
///
- internal sealed class CastingConverter : JsonConverter
+ internal sealed class CastingConverter : JsonConverter
{
- private readonly JsonConverter _sourceConverter;
+ private readonly JsonConverter _sourceConverter;
internal override Type? KeyType => _sourceConverter.KeyType;
internal override Type? ElementType => _sourceConverter.ElementType;
public override bool HandleNull { get; }
internal override bool SupportsCreateObjectDelegate => _sourceConverter.SupportsCreateObjectDelegate;
- internal CastingConverter(JsonConverter sourceConverter)
+ internal CastingConverter(JsonConverter sourceConverter, bool handleNull, bool handleNullOnRead, bool handleNullOnWrite)
{
- Debug.Assert(typeof(T).IsInSubtypeRelationshipWith(typeof(TSource)));
+ Debug.Assert(typeof(T).IsInSubtypeRelationshipWith(sourceConverter.TypeToConvert));
Debug.Assert(sourceConverter.SourceConverterForCastingConverter is null, "casting converters should not be layered.");
_sourceConverter = sourceConverter;
@@ -30,83 +31,45 @@ internal CastingConverter(JsonConverter sourceConverter)
CanBePolymorphic = sourceConverter.CanBePolymorphic;
// Ensure HandleNull values reflect the exact configuration of the source converter
- HandleNullOnRead = sourceConverter.HandleNullOnRead;
- HandleNullOnWrite = sourceConverter.HandleNullOnWrite;
- HandleNull = sourceConverter.HandleNull;
+ HandleNullOnRead = handleNullOnRead;
+ HandleNullOnWrite = handleNullOnWrite;
+ HandleNull = handleNull;
}
internal override JsonConverter? SourceConverterForCastingConverter => _sourceConverter;
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- => CastOnRead(_sourceConverter.Read(ref reader, typeToConvert, options));
+ => JsonSerializer.UnboxOnRead(_sourceConverter.ReadAsObject(ref reader, typeToConvert, options));
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
- => _sourceConverter.Write(writer, CastOnWrite(value), options);
+ => _sourceConverter.WriteAsObject(writer, value, options);
internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out T? value)
{
- bool result = _sourceConverter.OnTryRead(ref reader, typeToConvert, options, ref state, out TSource? sourceValue);
- value = CastOnRead(sourceValue);
+ bool result = _sourceConverter.OnTryReadAsObject(ref reader, typeToConvert, options, ref state, out object? sourceValue);
+ value = JsonSerializer.UnboxOnRead(sourceValue);
return result;
}
internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
- => _sourceConverter.OnTryWrite(writer, CastOnWrite(value), options, ref state);
+ => _sourceConverter.OnTryWriteAsObject(writer, value, options, ref state);
public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- => CastOnRead(_sourceConverter.ReadAsPropertyName(ref reader, typeToConvert, options));
+ => JsonSerializer.UnboxOnRead(_sourceConverter.ReadAsPropertyNameAsObject(ref reader, typeToConvert, options))!;
internal override T ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- => CastOnRead(_sourceConverter.ReadAsPropertyNameCore(ref reader, typeToConvert, options));
+ => JsonSerializer.UnboxOnRead(_sourceConverter.ReadAsPropertyNameCoreAsObject(ref reader, typeToConvert, options))!;
public override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
- => _sourceConverter.WriteAsPropertyName(writer, CastOnWrite(value), options);
+ => _sourceConverter.WriteAsPropertyNameAsObject(writer, value, options);
internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, T value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
- => _sourceConverter.WriteAsPropertyNameCore(writer, CastOnWrite(value), options, isWritingExtensionDataProperty);
+ => _sourceConverter.WriteAsPropertyNameCoreAsObject(writer, value, options, isWritingExtensionDataProperty);
internal override T ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options)
- => CastOnRead(_sourceConverter.ReadNumberWithCustomHandling(ref reader, handling, options));
+ => JsonSerializer.UnboxOnRead(_sourceConverter.ReadNumberWithCustomHandlingAsObject(ref reader, handling, options))!;
internal override void WriteNumberWithCustomHandling(Utf8JsonWriter writer, T value, JsonNumberHandling handling)
- => _sourceConverter.WriteNumberWithCustomHandling(writer, CastOnWrite(value), handling);
-
- private static T CastOnRead(TSource? source)
- {
- if (default(T) is null && default(TSource) is null && source is null)
- {
- return default!;
- }
-
- if (source is T t)
- {
- return t;
- }
-
- HandleFailure(source);
- return default!;
-
- static void HandleFailure(TSource? source)
- {
- if (source is null)
- {
- ThrowHelper.ThrowInvalidOperationException_DeserializeUnableToAssignNull(typeof(T));
- }
- else
- {
- ThrowHelper.ThrowInvalidCastException_DeserializeUnableToAssignValue(typeof(TSource), typeof(T));
- }
- }
- }
-
- private static TSource CastOnWrite(T source)
- {
- if (default(TSource) is not null && default(T) is null && source is null)
- {
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(typeof(TSource));
- }
-
- return (TSource)(object?)source!;
- }
+ => _sourceConverter.WriteNumberWithCustomHandlingAsObject(writer, value, handling);
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs
index c2d25718ba63b..d7153aeb94427 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs
@@ -166,7 +166,7 @@ internal override bool OnTryRead(
ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
{
Debug.Assert(!IsValueType);
- bool success = polymorphicConverter.OnTryReadAsObject(ref reader, options, ref state, out object? objectResult);
+ bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.TypeToConvert, options, ref state, out object? objectResult);
value = (TCollection)objectResult!;
state.ExitPolymorphicConverter(success);
return success;
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs
index 0475b0a8848df..e1dbc744f2180 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs
@@ -190,7 +190,7 @@ internal sealed override bool OnTryRead(
ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
{
Debug.Assert(!IsValueType);
- bool success = polymorphicConverter.OnTryReadAsObject(ref reader, options, ref state, out object? objectResult);
+ bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.TypeToConvert, options, ref state, out object? objectResult);
value = (TDictionary)objectResult!;
state.ExitPolymorphicConverter(success);
return success;
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs
index c3082bde9c419..f426e38217182 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs
@@ -107,7 +107,7 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
{
Debug.Assert(!IsValueType);
- bool success = polymorphicConverter.OnTryReadAsObject(ref reader, options, ref state, out object? objectResult);
+ bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.TypeToConvert, options, ref state, out object? objectResult);
value = (T)objectResult!;
state.ExitPolymorphicConverter(success);
return success;
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs
index f87356505ad78..c7ba3c959e32b 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs
@@ -19,7 +19,7 @@ protected sealed override bool ReadAndCacheConstructorArgument(scoped ref ReadSt
Debug.Assert(jsonParameterInfo.ShouldDeserialize);
Debug.Assert(jsonParameterInfo.Options != null);
- bool success = jsonParameterInfo.ConverterBase.TryReadAsObject(ref reader, jsonParameterInfo.Options!, ref state, out object? arg);
+ bool success = jsonParameterInfo.ConverterBase.TryReadAsObject(ref reader, TypeToConvert, jsonParameterInfo.Options!, ref state, out object? arg);
if (success && !(arg == null && jsonParameterInfo.IgnoreNullTokensOnRead))
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs
index d373ff9dfab6d..5a7f33e704726 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs
@@ -137,7 +137,7 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo
ResolvePolymorphicConverter(jsonTypeInfo, ref state) is JsonConverter polymorphicConverter)
{
Debug.Assert(!IsValueType);
- bool success = polymorphicConverter.OnTryReadAsObject(ref reader, options, ref state, out object? objectResult);
+ bool success = polymorphicConverter.OnTryReadAsObject(ref reader, polymorphicConverter.TypeToConvert, options, ref state, out object? objectResult);
value = (T)objectResult!;
state.ExitPolymorphicConverter(success);
return success;
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs
index 0fc70fd5b9818..ed2d884876c84 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs
@@ -110,6 +110,11 @@ internal virtual JsonTypeInfo CreateCustomJsonTypeInfo(JsonSerializerOptions opt
internal abstract JsonConverter CreateCastingConverter();
+ ///
+ /// Set if this converter is itself a casting converter.
+ ///
+ internal virtual JsonConverter? SourceConverterForCastingConverter => null;
+
internal abstract Type? ElementType { get; }
internal abstract Type? KeyType { get; }
@@ -138,15 +143,20 @@ internal static bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state)
// This is used internally to quickly determine the type being converted for JsonConverter.
internal abstract Type TypeToConvert { get; }
- internal abstract bool OnTryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value);
- internal abstract bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value);
+ internal abstract object? ReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options);
+ internal abstract bool OnTryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out object? value);
+ internal abstract bool TryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out object? value);
+ internal abstract object? ReadAsPropertyNameAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options);
+ internal abstract object? ReadAsPropertyNameCoreAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options);
+ internal abstract object? ReadNumberWithCustomHandlingAsObject(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options);
+ internal abstract void WriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options);
+ internal abstract bool OnTryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state);
internal abstract bool TryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state);
+ internal abstract void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options);
+ internal abstract void WriteAsPropertyNameCoreAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, bool isWritingExtensionDataProperty);
+ internal abstract void WriteNumberWithCustomHandlingAsObject(Utf8JsonWriter writer, object? value, JsonNumberHandling handling);
- ///
- /// Loosely-typed WriteToPropertyName() that forwards to strongly-typed WriteToPropertyName().
- ///
- internal abstract void WriteAsPropertyNameCoreAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, bool isWritingExtensionDataProperty);
// Whether a type (ConverterStrategy.Object) is deserialized using a parameterized constructor.
internal virtual bool ConstructorIsParameterized { get; }
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs
index b5c70e64bc243..441cbf5eab354 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs
@@ -61,8 +61,16 @@ internal JsonConverter GetConverterInternal(Type typeToConvert, JsonSerializerOp
return converter;
}
+ internal sealed override object? ReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
internal sealed override bool OnTryReadAsObject(
ref Utf8JsonReader reader,
+ Type typeToConvert,
JsonSerializerOptions options,
scoped ref ReadStack state,
out object? value)
@@ -74,6 +82,7 @@ internal sealed override bool OnTryReadAsObject(
internal sealed override bool TryReadAsObject(
ref Utf8JsonReader reader,
+ Type typeToConvert,
JsonSerializerOptions options,
scoped ref ReadStack state,
out object? value)
@@ -83,6 +92,45 @@ internal sealed override bool TryReadAsObject(
throw new InvalidOperationException();
}
+ internal sealed override object? ReadAsPropertyNameAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
+ internal sealed override object? ReadAsPropertyNameCoreAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
+ internal sealed override object? ReadNumberWithCustomHandlingAsObject(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
+ internal sealed override void WriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
+ internal sealed override bool OnTryWriteAsObject(
+ Utf8JsonWriter writer,
+ object? value,
+ JsonSerializerOptions options,
+ ref WriteStack state)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
internal sealed override bool TryWriteAsObject(
Utf8JsonWriter writer,
object? value,
@@ -94,10 +142,18 @@ internal sealed override bool TryWriteAsObject(
throw new InvalidOperationException();
}
+ internal sealed override void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
internal sealed override Type TypeToConvert => null!;
internal sealed override void WriteAsPropertyNameCoreAsObject(
- Utf8JsonWriter writer, object value,
+ Utf8JsonWriter writer,
+ object? value,
JsonSerializerOptions options,
bool isWritingExtensionDataProperty)
{
@@ -106,6 +162,13 @@ internal sealed override void WriteAsPropertyNameCoreAsObject(
throw new InvalidOperationException();
}
+ internal sealed override void WriteNumberWithCustomHandlingAsObject(Utf8JsonWriter writer, object? value, JsonNumberHandling handling)
+ {
+ Debug.Fail("We should never get here.");
+
+ throw new InvalidOperationException();
+ }
+
internal sealed override JsonConverter CreateCastingConverter()
{
ThrowHelper.ThrowInvalidOperationException_ConverterCanConvertMultipleTypes(typeof(TTarget), this);
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
index 972a33db7d347..33e3bb93ccca6 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
@@ -80,14 +80,9 @@ internal sealed override JsonConverter CreateCastingConverter(
// Avoid layering casting converters by consulting any source converters directly.
return
SourceConverterForCastingConverter?.CreateCastingConverter()
- ?? new CastingConverter(this);
+ ?? new CastingConverter(this, handleNull: HandleNull, handleNullOnRead: HandleNullOnRead, handleNullOnWrite: HandleNullOnWrite);
}
- ///
- /// Set if this converter is itself a casting converter.
- ///
- internal virtual JsonConverter? SourceConverterForCastingConverter => null;
-
internal override Type? KeyType => null;
internal override Type? ElementType => null;
@@ -128,10 +123,43 @@ public virtual bool HandleNull
///
internal bool HandleNullOnWrite { get; private protected set; }
+ // This non-generic API is sealed as it just forwards to the generic version.
+ internal sealed override void WriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
+ {
+ T valueOfT = JsonSerializer.UnboxOnWrite(value)!;
+ Write(writer, valueOfT, options);
+ }
+
+ // This non-generic API is sealed as it just forwards to the generic version.
+ internal sealed override bool OnTryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state)
+ {
+ T valueOfT = JsonSerializer.UnboxOnWrite(value)!;
+ return OnTryWrite(writer, valueOfT, options, ref state);
+ }
+
+ // This non-generic API is sealed as it just forwards to the generic version.
+ internal sealed override void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
+ {
+ T valueOfT = JsonSerializer.UnboxOnWrite(value)!;
+ WriteAsPropertyName(writer, valueOfT, options);
+ }
+
+ internal sealed override void WriteAsPropertyNameCoreAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
+ {
+ T valueOfT = JsonSerializer.UnboxOnWrite(value)!;
+ WriteAsPropertyNameCore(writer, valueOfT, options, isWritingExtensionDataProperty);
+ }
+
+ internal sealed override void WriteNumberWithCustomHandlingAsObject(Utf8JsonWriter writer, object? value, JsonNumberHandling handling)
+ {
+ T valueOfT = JsonSerializer.UnboxOnWrite(value)!;
+ WriteNumberWithCustomHandling(writer, valueOfT, handling);
+ }
+
// This non-generic API is sealed as it just forwards to the generic version.
internal sealed override bool TryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state)
{
- T valueOfT = (T)value!;
+ T valueOfT = JsonSerializer.UnboxOnWrite(value)!;
return TryWrite(writer, valueOfT, options, ref state);
}
@@ -284,20 +312,44 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali
return success;
}
- internal sealed override bool OnTryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value)
+ internal sealed override bool OnTryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out object? value)
{
- bool success = OnTryRead(ref reader, TypeToConvert, options, ref state, out T? typedValue);
+ bool success = OnTryRead(ref reader, typeToConvert, options, ref state, out T? typedValue);
value = typedValue;
return success;
}
- internal sealed override bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, scoped ref ReadStack state, out object? value)
+ internal sealed override bool TryReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out object? value)
{
- bool success = TryRead(ref reader, TypeToConvert, options, ref state, out T? typedValue);
+ bool success = TryRead(ref reader, typeToConvert, options, ref state, out T? typedValue);
value = typedValue;
return success;
}
+ internal sealed override object? ReadAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ T? typedValue = Read(ref reader, typeToConvert, options);
+ return typedValue;
+ }
+
+ internal sealed override object? ReadAsPropertyNameAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ T typedValue = ReadAsPropertyName(ref reader, typeToConvert, options);
+ return typedValue;
+ }
+
+ internal sealed override object? ReadAsPropertyNameCoreAsObject(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ T typedValue = ReadAsPropertyNameCore(ref reader, typeToConvert, options);
+ return typedValue;
+ }
+
+ internal sealed override object? ReadNumberWithCustomHandlingAsObject(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options)
+ {
+ T typedValue = ReadNumberWithCustomHandling(ref reader, handling, options);
+ return typedValue;
+ }
+
///
/// Performance optimization.
/// The 'in' modifier in 'TryWrite(in T Value)' causes boxing for Nullable{T}, so this helper avoids that.
@@ -628,9 +680,6 @@ internal virtual void WriteAsPropertyNameCore(Utf8JsonWriter writer, T value, Js
}
}
- internal sealed override void WriteAsPropertyNameCoreAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
- => WriteAsPropertyNameCore(writer, (T)value, options, isWritingExtensionDataProperty);
-
// .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
private JsonConverter? GetFallbackConverterForPropertyNameSerialization(JsonSerializerOptions options)
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs
index b0141c05428d2..d0f80b7d2839a 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs
@@ -81,5 +81,52 @@ internal static bool IsValidNumberHandlingValue(JsonNumberHandling handling) =>
internal static bool IsValidUnmappedMemberHandlingValue(JsonUnmappedMemberHandling handling) =>
handling is JsonUnmappedMemberHandling.Skip or JsonUnmappedMemberHandling.Disallow;
+
+ [return: NotNullIfNotNull(nameof(value))]
+ internal static T? UnboxOnRead(object? value)
+ {
+ if (value is null)
+ {
+ if (default(T) is not null)
+ {
+ // Casting null values to a non-nullable struct throws NullReferenceException.
+ ThrowUnableToCastValue(value);
+ }
+
+ return default;
+ }
+
+ if (value is T typedValue)
+ {
+ return typedValue;
+ }
+
+ ThrowUnableToCastValue(value);
+ return default!;
+
+ static void ThrowUnableToCastValue(object? value)
+ {
+ if (value is null)
+ {
+ ThrowHelper.ThrowInvalidOperationException_DeserializeUnableToAssignNull(declaredType: typeof(T));
+ }
+ else
+ {
+ ThrowHelper.ThrowInvalidCastException_DeserializeUnableToAssignValue(typeOfValue: value.GetType(), declaredType: typeof(T));
+ }
+ }
+ }
+
+ [return: NotNullIfNotNull(nameof(value))]
+ internal static T? UnboxOnWrite(object? value)
+ {
+ if (default(T) is not null && value is null)
+ {
+ // Casting null values to a non-nullable struct throws NullReferenceException.
+ ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(typeof(T));
+ }
+
+ return (T?)value;
+ }
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs
index cf3b8076dd1f5..d16480da79766 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.WriteHelpers.cs
@@ -277,32 +277,13 @@ rootValue is not null &&
}
internal sealed override void SerializeAsObject(Utf8JsonWriter writer, object? rootValue, bool isInvokedByPolymorphicConverter = false)
- => Serialize(writer, UnboxValue(rootValue), rootValue, isInvokedByPolymorphicConverter);
+ => Serialize(writer, JsonSerializer.UnboxOnWrite(rootValue), rootValue, isInvokedByPolymorphicConverter);
internal sealed override Task SerializeAsObjectAsync(Stream utf8Json, object? rootValue, CancellationToken cancellationToken, bool isInvokedByPolymorphicConverter = false)
- => SerializeAsync(utf8Json, UnboxValue(rootValue), cancellationToken, rootValue, isInvokedByPolymorphicConverter);
+ => SerializeAsync(utf8Json, JsonSerializer.UnboxOnWrite(rootValue), cancellationToken, rootValue, isInvokedByPolymorphicConverter);
internal sealed override void SerializeAsObject(Stream utf8Json, object? rootValue, bool isInvokedByPolymorphicConverter = false)
- => Serialize(utf8Json, UnboxValue(rootValue), rootValue, isInvokedByPolymorphicConverter);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private T? UnboxValue(object? value)
- {
- if (
-#if NETCOREAPP
- // Treated as a constant by recent versions of the JIT.
- typeof(T).IsValueType &&
-#else
- Type.IsValueType &&
-#endif
- default(T) is not null && value is null)
- {
- // Casting null values to a non-nullable struct throws NullReferenceException, replace with JsonException
- ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type);
- }
-
- return (T?)value;
- }
+ => Serialize(utf8Json, JsonSerializer.UnboxOnWrite(rootValue), rootValue, isInvokedByPolymorphicConverter);
// Fast-path serialization in source gen has not been designed with streaming in mind.
// Even though it's not used in streaming by default, we can sometimes try to turn it on