Skip to content

Commit

Permalink
Ensure JsonConverter<T>.Read/WriteAsPropertyName methods are user-cal…
Browse files Browse the repository at this point in the history
…lable. (#77488)

Also adds property name serialization for TimeSpan, TimeOnly, DateOnly, Uri and Version built-in converters.

Fix #77326
  • Loading branch information
eiriktsarpalis committed Oct 28, 2022
1 parent cac1b59 commit 8c496e7
Show file tree
Hide file tree
Showing 32 changed files with 301 additions and 61 deletions.
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
<Compile Include="System\Text\Json\Serialization\Converters\Collection\StackOrQueueConverterWithReflection.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonMetadataServicesConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Object\ObjectWithParameterizedConstructorConverter.Large.Reflection.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Value\JsonPrimitiveConverter.cs" />
<Compile Include="System\Text\Json\Serialization\IgnoreReferenceResolver.cs" />
<Compile Include="System\Text\Json\Serialization\IJsonOnDeserialized.cs" />
<Compile Include="System\Text\Json\Serialization\IJsonOnDeserializing.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,28 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert,
return true;
}

public override object ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
return null!;
}

internal override object ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
return null!;
}

public override void WriteAsPropertyName(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
{
if (value is null)
{
ThrowHelper.ThrowArgumentNullException(nameof(value));
}

WriteAsPropertyNameCore(writer, value, options, isWritingExtensionDataProperty: false);
}

internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
{
// This converter does not handle nulls.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class BooleanConverter : JsonConverter<bool>
internal sealed class BooleanConverter : JsonPrimitiveConverter<bool>
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -20,6 +20,7 @@ public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOpti

internal override bool ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
ReadOnlySpan<byte> propertyName = reader.GetSpan();
if (!(Utf8Parser.TryParse(propertyName, out bool value, out int bytesConsumed)
&& propertyName.Length == bytesConsumed))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class ByteConverter : JsonConverter<byte>
internal sealed class ByteConverter : JsonPrimitiveConverter<byte>
{
public ByteConverter()
{
Expand All @@ -22,6 +24,7 @@ public override void Write(Utf8JsonWriter writer, byte value, JsonSerializerOpti

internal override byte ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetByteWithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class CharConverter : JsonConverter<char>
internal sealed class CharConverter : JsonPrimitiveConverter<char>
{
private const int MaxEscapedCharacterLength = JsonConstants.MaxExpansionFactorWhileEscaping;

Expand Down Expand Up @@ -40,7 +40,10 @@ public override void Write(Utf8JsonWriter writer, char value, JsonSerializerOpti
}

internal override char ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Read(ref reader, typeToConvert, options);
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return Read(ref reader, typeToConvert, options);
}

internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, char value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DateOnlyConverter : JsonConverter<DateOnly>
internal sealed class DateOnlyConverter : JsonPrimitiveConverter<DateOnly>
{
public const int FormatLength = 10; // YYYY-MM-DD
public const int MaxEscapedFormatLength = FormatLength * JsonConstants.MaxExpansionFactorWhileEscaping;
Expand All @@ -24,10 +24,11 @@ public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, Jso

internal override DateOnly ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return ReadCore(ref reader);
}

private DateOnly ReadCore(ref Utf8JsonReader reader)
private static DateOnly ReadCore(ref Utf8JsonReader reader)
{
if (!JsonHelpers.IsInRangeInclusive(reader.ValueLength, FormatLength, MaxEscapedFormatLength))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DateTimeConverter : JsonConverter<DateTime>
internal sealed class DateTimeConverter : JsonPrimitiveConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -17,6 +19,7 @@ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializer

internal override DateTime ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetDateTimeNoValidation();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
internal sealed class DateTimeOffsetConverter : JsonPrimitiveConverter<DateTimeOffset>
{
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -17,6 +19,7 @@ public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSeri

internal override DateTimeOffset ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetDateTimeOffsetNoValidation();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DecimalConverter : JsonConverter<decimal>
internal sealed class DecimalConverter : JsonPrimitiveConverter<decimal>
{
public DecimalConverter()
{
Expand All @@ -22,6 +24,7 @@ public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerO

internal override decimal ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetDecimalWithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class DoubleConverter : JsonConverter<double>
internal sealed class DoubleConverter : JsonPrimitiveConverter<double>
{
public DoubleConverter()
{
Expand All @@ -22,6 +24,7 @@ public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOp

internal override double ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetDoubleWithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace System.Text.Json.Serialization.Converters
{
internal sealed class EnumConverter<T> : JsonConverter<T>
internal sealed class EnumConverter<T> : JsonPrimitiveConverter<T>
where T : struct, Enum
{
private static readonly TypeCode s_enumTypeCode = Type.GetTypeCode(typeof(T));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class GuidConverter : JsonConverter<Guid>
internal sealed class GuidConverter : JsonPrimitiveConverter<Guid>
{
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -17,6 +19,7 @@ public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOpti

internal override Guid ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetGuidNoValidation();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class Int16Converter : JsonConverter<short>
internal sealed class Int16Converter : JsonPrimitiveConverter<short>
{
public Int16Converter()
{
Expand All @@ -23,6 +25,7 @@ public override void Write(Utf8JsonWriter writer, short value, JsonSerializerOpt

internal override short ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetInt16WithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class Int32Converter : JsonConverter<int>
internal sealed class Int32Converter : JsonPrimitiveConverter<int>
{
public Int32Converter()
{
Expand All @@ -23,6 +25,7 @@ public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptio

internal override int ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetInt32WithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class Int64Converter : JsonConverter<long>
internal sealed class Int64Converter : JsonPrimitiveConverter<long>
{
public Int64Converter()
{
Expand All @@ -22,6 +24,7 @@ public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOpti

internal override long ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetInt64WithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Text.Json.Serialization.Converters
{
/// <summary>
/// Inherited by built-in converters serializing types as JSON primitives that support property name serialization.
/// </summary>
internal abstract class JsonPrimitiveConverter<T> : JsonConverter<T>
{
public sealed override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value is null)
{
ThrowHelper.ThrowArgumentNullException(nameof(value));
}

WriteAsPropertyNameCore(writer, value, options, isWritingExtensionDataProperty: false);
}

public sealed override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.PropertyName)
{
ThrowHelper.ThrowInvalidOperationException_ExpectedPropertyName(reader.TokenType);
}

return ReadAsPropertyNameCore(ref reader, typeToConvert, options);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class SByteConverter : JsonConverter<sbyte>
internal sealed class SByteConverter : JsonPrimitiveConverter<sbyte>
{
public SByteConverter()
{
Expand All @@ -22,6 +24,7 @@ public override void Write(Utf8JsonWriter writer, sbyte value, JsonSerializerOpt

internal override sbyte ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetSByteWithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class SingleConverter : JsonConverter<float>
internal sealed class SingleConverter : JsonPrimitiveConverter<float>
{

public SingleConverter()
Expand All @@ -23,6 +25,7 @@ public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOpt

internal override float ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetSingleWithQuotes();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Text.Json.Serialization.Converters
{
internal sealed class StringConverter : JsonConverter<string>
internal sealed class StringConverter : JsonPrimitiveConverter<string>
{
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Expand All @@ -25,6 +27,7 @@ public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerO

internal override string ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetString()!;
}

Expand Down
Loading

0 comments on commit 8c496e7

Please sign in to comment.