Skip to content

Commit 2eaf1d2

Browse files
Move the CreateCastingConverter<T>() method to the base JsonConverter type. (#87211)
1 parent 22c16ca commit 2eaf1d2

File tree

4 files changed

+47
-53
lines changed

4 files changed

+47
-53
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/CastingConverter.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal sealed class CastingConverter<T> : JsonConverter<T>
1919
public override bool HandleNull { get; }
2020
internal override bool SupportsCreateObjectDelegate => _sourceConverter.SupportsCreateObjectDelegate;
2121

22-
internal CastingConverter(JsonConverter sourceConverter, bool handleNull, bool handleNullOnRead, bool handleNullOnWrite)
22+
internal CastingConverter(JsonConverter sourceConverter)
2323
{
2424
Debug.Assert(typeof(T).IsInSubtypeRelationshipWith(sourceConverter.TypeToConvert));
2525
Debug.Assert(sourceConverter.SourceConverterForCastingConverter is null, "casting converters should not be layered.");
@@ -31,9 +31,9 @@ internal CastingConverter(JsonConverter sourceConverter, bool handleNull, bool h
3131
CanBePolymorphic = sourceConverter.CanBePolymorphic;
3232

3333
// Ensure HandleNull values reflect the exact configuration of the source converter
34-
HandleNullOnRead = handleNullOnRead;
35-
HandleNullOnWrite = handleNullOnWrite;
36-
HandleNull = handleNull;
34+
HandleNullOnRead = sourceConverter.HandleNullOnRead;
35+
HandleNullOnWrite = sourceConverter.HandleNullOnWrite;
36+
HandleNull = sourceConverter.HandleNullOnWrite;
3737
}
3838

3939
internal override JsonConverter? SourceConverterForCastingConverter => _sourceConverter;

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,41 @@ internal virtual JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions options)
102102
throw new InvalidOperationException();
103103
}
104104

105-
internal abstract JsonConverter<TTarget> CreateCastingConverter<TTarget>();
105+
internal JsonConverter<TTarget> CreateCastingConverter<TTarget>()
106+
{
107+
Debug.Assert(this is not JsonConverterFactory);
108+
109+
if (this is JsonConverter<TTarget> conv)
110+
{
111+
return conv;
112+
}
113+
else
114+
{
115+
JsonSerializerOptions.CheckConverterNullabilityIsSameAsPropertyType(this, typeof(TTarget));
116+
117+
// Avoid layering casting converters by consulting any source converters directly.
118+
return
119+
SourceConverterForCastingConverter?.CreateCastingConverter<TTarget>()
120+
?? new CastingConverter<TTarget>(this);
121+
}
122+
}
123+
124+
/// <summary>
125+
/// Tracks whether the JsonConverter&lt;T&gt;.HandleNull property has been overridden by a derived converter.
126+
/// </summary>
127+
internal bool UsesDefaultHandleNull { get; private protected set; }
128+
129+
/// <summary>
130+
/// Does the converter want to be called when reading null tokens.
131+
/// When JsonConverter&lt;T&gt;.HandleNull isn't overridden this can still be true for non-nullable structs.
132+
/// </summary>
133+
internal bool HandleNullOnRead { get; private protected init; }
134+
135+
/// <summary>
136+
/// Does the converter want to be called for null values.
137+
/// Should always match the precise value of the JsonConverter&lt;T&gt;.HandleNull virtual property.
138+
/// </summary>
139+
internal bool HandleNullOnWrite { get; private protected init; }
106140

107141
/// <summary>
108142
/// Set if this converter is itself a casting converter.

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,5 @@ internal sealed override void WriteNumberWithCustomHandlingAsObject(Utf8JsonWrit
161161

162162
throw new InvalidOperationException();
163163
}
164-
165-
internal sealed override JsonConverter<TTarget> CreateCastingConverter<TTarget>()
166-
{
167-
ThrowHelper.ThrowInvalidOperationException_ConverterCanConvertMultipleTypes(typeof(TTarget), this);
168-
return null!;
169-
}
170164
}
171165
}

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ protected internal JsonConverter()
2626
HandleNullOnRead = true;
2727
HandleNullOnWrite = true;
2828
}
29-
else
29+
else if (UsesDefaultHandleNull)
3030
{
31-
// For the HandleNull == false case, either:
32-
// 1) The default values are assigned in this type's virtual HandleNull property
33-
// or
34-
// 2) A converter overrode HandleNull and returned false so HandleNullOnRead and HandleNullOnWrite
35-
// will be their default values of false.
31+
// If the type doesn't support null, allow the converter a chance to modify.
32+
// These semantics are backwards compatible with 3.0.
33+
HandleNullOnRead = default(T) is not null;
34+
35+
// The framework handles null automatically on writes.
36+
HandleNullOnWrite = false;
3637
}
3738
}
3839

@@ -56,21 +57,6 @@ internal sealed override JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions o
5657
return new JsonTypeInfo<T>(this, options);
5758
}
5859

59-
internal sealed override JsonConverter<TTarget> CreateCastingConverter<TTarget>()
60-
{
61-
if (this is JsonConverter<TTarget> conv)
62-
{
63-
return conv;
64-
}
65-
66-
JsonSerializerOptions.CheckConverterNullabilityIsSameAsPropertyType(this, typeof(TTarget));
67-
68-
// Avoid layering casting converters by consulting any source converters directly.
69-
return
70-
SourceConverterForCastingConverter?.CreateCastingConverter<TTarget>()
71-
?? new CastingConverter<TTarget>(this, handleNull: HandleNull, handleNullOnRead: HandleNullOnRead, handleNullOnWrite: HandleNullOnWrite);
72-
}
73-
7460
internal override Type? KeyType => null;
7561

7662
internal override Type? ElementType => null;
@@ -86,31 +72,11 @@ public virtual bool HandleNull
8672
{
8773
get
8874
{
89-
// HandleNull is only called by the framework once during initialization and any
90-
// subsequent calls elsewhere would just re-initialize to the same values (we don't
91-
// track a "hasInitialized" flag since that isn't necessary).
92-
93-
// If the type doesn't support null, allow the converter a chance to modify.
94-
// These semantics are backwards compatible with 3.0.
95-
HandleNullOnRead = default(T) is not null;
96-
97-
// The framework handles null automatically on writes.
98-
HandleNullOnWrite = false;
99-
75+
UsesDefaultHandleNull = true;
10076
return false;
10177
}
10278
}
10379

104-
/// <summary>
105-
/// Does the converter want to be called when reading null tokens.
106-
/// </summary>
107-
internal bool HandleNullOnRead { get; private protected set; }
108-
109-
/// <summary>
110-
/// Does the converter want to be called for null values.
111-
/// </summary>
112-
internal bool HandleNullOnWrite { get; private protected set; }
113-
11480
// This non-generic API is sealed as it just forwards to the generic version.
11581
internal sealed override void WriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
11682
{

0 commit comments

Comments
 (0)