Skip to content

Commit

Permalink
Remove second type parameter from CastingConverter (#80755)
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis committed Jan 20, 2023
1 parent 43b8dcf commit a04aaeb
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,26 @@
// 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
{
/// <summary>
/// Converter wrapper which casts SourceType into TargetType
/// </summary>
internal sealed class CastingConverter<T, TSource> : JsonConverter<T>
internal sealed class CastingConverter<T> : JsonConverter<T>
{
private readonly JsonConverter<TSource> _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<TSource> 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;
Expand All @@ -30,83 +31,45 @@ internal CastingConverter(JsonConverter<TSource> 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<T>(_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<T>(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<T>(_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<T>(_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<T>(_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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ internal virtual JsonTypeInfo CreateCustomJsonTypeInfo(JsonSerializerOptions opt

internal abstract JsonConverter<TTarget> CreateCastingConverter<TTarget>();

/// <summary>
/// Set if this converter is itself a casting converter.
/// </summary>
internal virtual JsonConverter? SourceConverterForCastingConverter => null;

internal abstract Type? ElementType { get; }

internal abstract Type? KeyType { get; }
Expand Down Expand Up @@ -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<T>.
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);

/// <summary>
/// Loosely-typed WriteToPropertyName() that forwards to strongly-typed WriteToPropertyName().
/// </summary>
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; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -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)
{
Expand All @@ -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<TTarget> CreateCastingConverter<TTarget>()
{
ThrowHelper.ThrowInvalidOperationException_ConverterCanConvertMultipleTypes(typeof(TTarget), this);
Expand Down
Loading

0 comments on commit a04aaeb

Please sign in to comment.