Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add opt-in for converters to handle null #35826

Merged
merged 11 commits into from
May 8, 2020
2 changes: 2 additions & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,8 @@ public abstract partial class JsonConverter<T> : System.Text.Json.Serialization.
{
protected internal JsonConverter() { }
public override bool CanConvert(System.Type typeToConvert) { throw null; }
public virtual bool HandleNull { get { throw null; } }
[return: System.Diagnostics.CodeAnalysis.MaybeNull]
jozkee marked this conversation as resolved.
Show resolved Hide resolved
public abstract T Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options);
public abstract void Write(System.Text.Json.Utf8JsonWriter writer, T value, System.Text.Json.JsonSerializerOptions options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -16,9 +17,9 @@ internal sealed class ArrayConverter<TCollection, TElement>
{
internal override bool CanHaveIdMetadata => false;

protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
layomia marked this conversation as resolved.
Show resolved Hide resolved
{
((List<TElement>)state.Current.ReturnValue!).Add(value);
((List<TElement>)state.Current.ReturnValue!).Add(value!);
layomia marked this conversation as resolved.
Show resolved Hide resolved
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class ConcurrentQueueOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ConcurrentQueue<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Enqueue(value);
((TCollection)state.Current.ReturnValue!).Enqueue(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class ConcurrentStackOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ConcurrentStack<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Push(value);
((TCollection)state.Current.ReturnValue!).Push(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal abstract class DictionaryDefaultConverter<TCollection, TValue>
/// <summary>
/// When overridden, adds the value to the collection.
/// </summary>
protected abstract void Add(TValue value, JsonSerializerOptions options, ref ReadStack state);
protected abstract void Add([AllowNull] TValue value, JsonSerializerOptions options, ref ReadStack state);

/// <summary>
/// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -15,10 +15,10 @@ internal sealed class DictionaryOfStringTValueConverter<TCollection, TValue>
: DictionaryDefaultConverter<TCollection, TValue>
where TCollection : Dictionary<string, TValue>
{
protected override void Add(TValue value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add([AllowNull] TValue value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((TCollection)state.Current.ReturnValue!)[key] = value;
((TCollection)state.Current.ReturnValue!)[key] = value!;
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -13,9 +14,9 @@ internal sealed class ICollectionOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ICollection<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((ICollection<TElement>)state.Current.ReturnValue!).Add(value);
((ICollection<TElement>)state.Current.ReturnValue!).Add(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -14,10 +15,10 @@ internal sealed class IDictionaryOfStringTValueConverter<TCollection, TValue>
: DictionaryDefaultConverter<TCollection, TValue>
where TCollection : IDictionary<string, TValue>
{
protected override void Add(TValue value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add([AllowNull] TValue value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((TCollection)state.Current.ReturnValue!)[key] = value;
((TCollection)state.Current.ReturnValue!)[key] = value!;
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace System.Text.Json.Serialization.Converters
internal abstract class IEnumerableDefaultConverter<TCollection, TElement>
: JsonCollectionConverter<TCollection, TElement>
{
protected abstract void Add(TElement value, ref ReadStack state);
protected abstract void Add([AllowNull] TElement value, ref ReadStack state);
protected abstract void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options);
protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }

Expand Down Expand Up @@ -66,7 +66,7 @@ internal override bool OnTryRead(
}

// Obtain the CLR value from the JSON and apply to the object.
TElement element = elementConverter.Read(ref reader, elementConverter.TypeToConvert, options);
TElement element = elementConverter.Read(ref reader, elementConverter.TypeToConvert, options)!;
Add(element, ref state);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -14,9 +15,9 @@ internal sealed class IEnumerableOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : IEnumerable<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((List<TElement>)state.Current.ReturnValue!).Add(value);
((List<TElement>)state.Current.ReturnValue!).Add(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -13,9 +14,9 @@ internal sealed class IListOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : IList<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Add(value);
((TCollection)state.Current.ReturnValue!).Add(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class IReadOnlyDictionaryOfStringTValueConverter<TCollection, TValue>
: DictionaryDefaultConverter<TCollection, TValue>
where TCollection : IReadOnlyDictionary<string, TValue>
{
protected override void Add(TValue value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add([AllowNull] TValue value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((Dictionary<string, TValue>)state.Current.ReturnValue!)[key] = value;
((Dictionary<string, TValue>)state.Current.ReturnValue!)[key] = value!;
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class ISetOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ISet<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Add(value);
((TCollection)state.Current.ReturnValue!).Add(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class ImmutableDictionaryOfStringTValueConverter<TCollection, TValue>
: DictionaryDefaultConverter<TCollection, TValue>
where TCollection : IReadOnlyDictionary<string, TValue>
{
protected override void Add(TValue value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add([AllowNull] TValue value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((Dictionary<string, TValue>)state.Current.ReturnValue!)[key] = value;
((Dictionary<string, TValue>)state.Current.ReturnValue!)[key] = value!;
}

internal override bool CanHaveIdMetadata => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class ImmutableEnumerableOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : IEnumerable<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((List<TElement>)state.Current.ReturnValue!).Add(value);
((List<TElement>)state.Current.ReturnValue!).Add(value!);
}

internal override bool CanHaveIdMetadata => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
Expand All @@ -11,9 +12,9 @@ internal sealed class ListOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection: List<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Add(value);
((TCollection)state.Current.ReturnValue!).Add(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class QueueOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : Queue<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Enqueue(value);
((TCollection)state.Current.ReturnValue!).Enqueue(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class StackOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : Stack<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add([AllowNull] TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Push(value);
((TCollection)state.Current.ReturnValue!).Push(value!);
}

protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ internal override bool OnTryRead(
ThrowHelper.ThrowJsonException();
}

value = new KeyValuePair<TKey, TValue>(k, v);
value = new KeyValuePair<TKey, TValue>(k!, v!);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public NullableConverter(JsonConverter<T> converter)

public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// We do not check _converter.HandleNull, as the underlying struct cannot be null.
// A custom converter for some type T? can handle null.
if (reader.TokenType == JsonTokenType.Null)
{
return null;
Expand All @@ -30,6 +32,8 @@ public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOption
{
if (!value.HasValue)
{
// We do not check _converter.HandleNull, as the underlying struct cannot be null.
// A custom converter for some type T? can handle null.
writer.WriteNullValue();
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,6 @@ internal JsonConverter() { }

internal abstract Type? ElementType { get; }

/// <summary>
/// Cached value of ShouldHandleNullValue. It is cached since the converter should never
/// change the value depending on state and because it may contain non-trival logic.
/// </summary>
internal bool HandleNullValue { get; set; }

/// <summary>
/// Cached value of TypeToConvert.IsValueType, which is an expensive call.
/// </summary>
Expand Down
Loading