diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
index 6691df19ae1939..bcd039e90cbfa7 100644
--- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj
+++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
@@ -111,6 +111,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
+
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs
index 62835f45b94061..3ca47a7ebba490 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectConverter.cs
@@ -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.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/BooleanConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/BooleanConverter.cs
index 1345659c0f64fc..c12f9447bb0053 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/BooleanConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/BooleanConverter.cs
@@ -6,7 +6,7 @@
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class BooleanConverter : JsonConverter
+ internal sealed class BooleanConverter : JsonPrimitiveConverter
{
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
@@ -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 propertyName = reader.GetSpan();
if (!(Utf8Parser.TryParse(propertyName, out bool value, out int bytesConsumed)
&& propertyName.Length == bytesConsumed))
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteConverter.cs
index c927119345d937..9df14e2060976c 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteConverter.cs
@@ -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
+ internal sealed class ByteConverter : JsonPrimitiveConverter
{
public ByteConverter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/CharConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/CharConverter.cs
index 5b829b49084986..b3caa32d186bca 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/CharConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/CharConverter.cs
@@ -6,7 +6,7 @@
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class CharConverter : JsonConverter
+ internal sealed class CharConverter : JsonPrimitiveConverter
{
private const int MaxEscapedCharacterLength = JsonConstants.MaxExpansionFactorWhileEscaping;
@@ -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)
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateOnlyConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateOnlyConverter.cs
index 0aaec4759d0b75..16b4e5e82ec6d1 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateOnlyConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateOnlyConverter.cs
@@ -7,7 +7,7 @@
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class DateOnlyConverter : JsonConverter
+ internal sealed class DateOnlyConverter : JsonPrimitiveConverter
{
public const int FormatLength = 10; // YYYY-MM-DD
public const int MaxEscapedFormatLength = FormatLength * JsonConstants.MaxExpansionFactorWhileEscaping;
@@ -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))
{
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeConverter.cs
index 85cb86b15e0cd8..204d39551cea29 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeConverter.cs
@@ -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
+ internal sealed class DateTimeConverter : JsonPrimitiveConverter
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeOffsetConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeOffsetConverter.cs
index 82a6581be80e49..6cfeaf4c38edcd 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeOffsetConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DateTimeOffsetConverter.cs
@@ -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
+ internal sealed class DateTimeOffsetConverter : JsonPrimitiveConverter
{
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DecimalConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DecimalConverter.cs
index 52c94458141bee..7b3ff9fade01d6 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DecimalConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DecimalConverter.cs
@@ -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
+ internal sealed class DecimalConverter : JsonPrimitiveConverter
{
public DecimalConverter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DoubleConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DoubleConverter.cs
index 34499fb350042a..0c6a6c4e26cc65 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DoubleConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/DoubleConverter.cs
@@ -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
+ internal sealed class DoubleConverter : JsonPrimitiveConverter
{
public DoubleConverter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs
index d9de4d1f0fbe4a..7b002db36f83bc 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs
@@ -10,7 +10,7 @@
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class EnumConverter : JsonConverter
+ internal sealed class EnumConverter : JsonPrimitiveConverter
where T : struct, Enum
{
private static readonly TypeCode s_enumTypeCode = Type.GetTypeCode(typeof(T));
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/GuidConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/GuidConverter.cs
index b0c358b67aa762..c808bc6d730ba3 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/GuidConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/GuidConverter.cs
@@ -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
+ internal sealed class GuidConverter : JsonPrimitiveConverter
{
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int16Converter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int16Converter.cs
index 2ada98b75c0841..73da887f71c922 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int16Converter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int16Converter.cs
@@ -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
+ internal sealed class Int16Converter : JsonPrimitiveConverter
{
public Int16Converter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int32Converter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int32Converter.cs
index 49ba9d15f3cc37..a243b0d65f26e5 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int32Converter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int32Converter.cs
@@ -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
+ internal sealed class Int32Converter : JsonPrimitiveConverter
{
public Int32Converter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int64Converter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int64Converter.cs
index 038deb5524705d..7987daf7c6916c 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int64Converter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/Int64Converter.cs
@@ -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
+ internal sealed class Int64Converter : JsonPrimitiveConverter
{
public Int64Converter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/JsonPrimitiveConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/JsonPrimitiveConverter.cs
new file mode 100644
index 00000000000000..8da98cb141fef0
--- /dev/null
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/JsonPrimitiveConverter.cs
@@ -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
+{
+ ///
+ /// Inherited by built-in converters serializing types as JSON primitives that support property name serialization.
+ ///
+ internal abstract class JsonPrimitiveConverter : JsonConverter
+ {
+ 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);
+ }
+ }
+}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SByteConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SByteConverter.cs
index 198ec2d9e6e9bd..f302fd6a04565a 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SByteConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SByteConverter.cs
@@ -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
+ internal sealed class SByteConverter : JsonPrimitiveConverter
{
public SByteConverter()
{
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SingleConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SingleConverter.cs
index 5ce4cf603016a7..4c6b2c5a80674e 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SingleConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/SingleConverter.cs
@@ -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
+ internal sealed class SingleConverter : JsonPrimitiveConverter
{
public SingleConverter()
@@ -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();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/StringConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/StringConverter.cs
index a2ef84679e4267..2984a0a5bc9f6a 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/StringConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/StringConverter.cs
@@ -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
+ internal sealed class StringConverter : JsonPrimitiveConverter
{
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
@@ -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()!;
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeOnlyConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeOnlyConverter.cs
index 73cb0c6012720c..29ee02876ac624 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeOnlyConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeOnlyConverter.cs
@@ -6,7 +6,7 @@
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class TimeOnlyConverter : JsonConverter
+ internal sealed class TimeOnlyConverter : JsonPrimitiveConverter
{
private const int MinimumTimeOnlyFormatLength = 8; // hh:mm:ss
private const int MaximumTimeOnlyFormatLength = 16; // hh:mm:ss.fffffff
@@ -19,6 +19,19 @@ public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, Jso
ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType);
}
+ return ReadCore(ref reader);
+ }
+
+ internal override TimeOnly ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
+ return ReadCore(ref reader);
+ }
+
+ private static TimeOnly ReadCore(ref Utf8JsonReader reader)
+ {
+ Debug.Assert(reader.TokenType is JsonTokenType.String or JsonTokenType.PropertyName);
+
if (!JsonHelpers.IsInRangeInclusive(reader.ValueLength, MinimumTimeOnlyFormatLength, MaximumEscapedTimeOnlyFormatLength))
{
ThrowHelper.ThrowFormatException(DataType.TimeOnly);
@@ -70,5 +83,15 @@ public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializer
writer.WriteStringValue(output.Slice(0, bytesWritten));
}
+
+ internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
+ {
+ Span output = stackalloc byte[MaximumTimeOnlyFormatLength];
+
+ bool result = Utf8Formatter.TryFormat(value.ToTimeSpan(), output, out int bytesWritten, 'c');
+ Debug.Assert(result);
+
+ writer.WritePropertyName(output.Slice(0, bytesWritten));
+ }
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs
index c956080c6885b9..245f5ea4afc1f2 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/TimeSpanConverter.cs
@@ -1,13 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class TimeSpanConverter : JsonConverter
+ internal sealed class TimeSpanConverter : JsonPrimitiveConverter
{
private const int MinimumTimeSpanFormatLength = 8; // hh:mm:ss
private const int MaximumTimeSpanFormatLength = 26; // -dddddddd.hh:mm:ss.fffffff
@@ -20,6 +19,19 @@ public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, Jso
ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType);
}
+ return ReadCore(ref reader);
+ }
+
+ internal override TimeSpan ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
+ return ReadCore(ref reader);
+ }
+
+ private static TimeSpan ReadCore(ref Utf8JsonReader reader)
+ {
+ Debug.Assert(reader.TokenType is JsonTokenType.String or JsonTokenType.PropertyName);
+
if (!JsonHelpers.IsInRangeInclusive(reader.ValueLength, MinimumTimeSpanFormatLength, MaximumEscapedTimeSpanFormatLength))
{
ThrowHelper.ThrowFormatException(DataType.TimeSpan);
@@ -69,5 +81,15 @@ public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializer
writer.WriteStringValue(output.Slice(0, bytesWritten));
}
+
+ internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
+ {
+ Span output = stackalloc byte[MaximumTimeSpanFormatLength];
+
+ bool result = Utf8Formatter.TryFormat(value, output, out int bytesWritten, 'c');
+ Debug.Assert(result);
+
+ writer.WritePropertyName(output.Slice(0, bytesWritten));
+ }
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt16Converter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt16Converter.cs
index 0ac6a644faf760..16c717cb7d440d 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt16Converter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt16Converter.cs
@@ -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 UInt16Converter : JsonConverter
+ internal sealed class UInt16Converter : JsonPrimitiveConverter
{
public UInt16Converter()
{
@@ -23,6 +25,7 @@ public override void Write(Utf8JsonWriter writer, ushort value, JsonSerializerOp
internal override ushort ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
+ Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetUInt16WithQuotes();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt32Converter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt32Converter.cs
index c0d2a754ee062c..9a8dccb9b80cf6 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt32Converter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt32Converter.cs
@@ -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 UInt32Converter : JsonConverter
+ internal sealed class UInt32Converter : JsonPrimitiveConverter
{
public UInt32Converter()
{
@@ -23,6 +25,7 @@ public override void Write(Utf8JsonWriter writer, uint value, JsonSerializerOpti
internal override uint ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
+ Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetUInt32WithQuotes();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt64Converter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt64Converter.cs
index f63d9175abb390..7b3290a4787589 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt64Converter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UInt64Converter.cs
@@ -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 UInt64Converter : JsonConverter
+ internal sealed class UInt64Converter : JsonPrimitiveConverter
{
public UInt64Converter()
{
@@ -22,6 +24,7 @@ public override void Write(Utf8JsonWriter writer, ulong value, JsonSerializerOpt
internal override ulong ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
+ Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
return reader.GetUInt64WithQuotes();
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UriConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UriConverter.cs
index b256436b0c5c09..e50a6521abda30 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UriConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/UriConverter.cs
@@ -1,9 +1,12 @@
// 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;
+using System.Runtime.CompilerServices;
+
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class UriConverter : JsonConverter
+ internal sealed class UriConverter : JsonPrimitiveConverter
{
public override Uri Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
@@ -21,5 +24,16 @@ public override void Write(Utf8JsonWriter writer, Uri value, JsonSerializerOptio
{
writer.WriteStringValue(value.OriginalString);
}
+
+ internal override Uri ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Assert(reader.TokenType is JsonTokenType.PropertyName);
+ return Read(ref reader, typeToConvert, options);
+ }
+
+ internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, Uri value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
+ {
+ writer.WritePropertyName(value.OriginalString);
+ }
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/VersionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/VersionConverter.cs
index 4343672f429117..fa4d0a07dbb9e7 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/VersionConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/VersionConverter.cs
@@ -1,13 +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.Buffers;
-using System.Buffers.Text;
using System.Diagnostics;
namespace System.Text.Json.Serialization.Converters
{
- internal sealed class VersionConverter : JsonConverter
+ internal sealed class VersionConverter : JsonPrimitiveConverter
{
#if NETCOREAPP
private const int MinimumVersionLength = 3; // 0.0
@@ -24,6 +22,13 @@ public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, Json
ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType);
}
+ return ReadCore(ref reader);
+ }
+
+ private static Version ReadCore(ref Utf8JsonReader reader)
+ {
+ Debug.Assert(reader.TokenType is JsonTokenType.PropertyName or JsonTokenType.String);
+
#if NETCOREAPP
if (!JsonHelpers.IsInRangeInclusive(reader.ValueLength, MinimumVersionLength, MaximumEscapedVersionLength))
{
@@ -75,6 +80,23 @@ public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerO
writer.WriteStringValue(span.Slice(0, charsWritten));
#else
writer.WriteStringValue(value.ToString());
+#endif
+ }
+
+ internal override Version ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return ReadCore(ref reader);
+ }
+
+ internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, Version value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
+ {
+#if NETCOREAPP
+ Span span = stackalloc char[MaximumVersionLength];
+ bool formattedSuccessfully = value.TryFormat(span, out int charsWritten);
+ Debug.Assert(formattedSuccessfully && charsWritten >= MinimumVersionLength);
+ writer.WritePropertyName(span.Slice(0, charsWritten));
+#else
+ writer.WritePropertyName(value.ToString());
#endif
}
}
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 c69ba39daf4dd8..dce96390de41c5 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
@@ -583,19 +583,14 @@ public abstract void Write(
/// Method should be overridden in custom converters of types used in deserialized dictionary keys.
public virtual T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- if (!IsInternalConverter &&
- options.SerializerContext is null && // For consistency do not return any default converters for
- // options instances linked to a JsonSerializerContext,
- // even if the default converters might have been rooted.
- DefaultJsonTypeInfoResolver.TryGetDefaultSimpleConverter(TypeToConvert, out JsonConverter? defaultConverter))
+ // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
+ JsonConverter? fallbackConverter = GetFallbackConverterForPropertyNameSerialization(options);
+ if (fallbackConverter is null)
{
- // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
- Debug.Assert(defaultConverter.IsInternalConverter && defaultConverter is JsonConverter);
- return ((JsonConverter)defaultConverter).ReadAsPropertyNameCore(ref reader, TypeToConvert, options);
+ ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
}
- ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
- return default;
+ return fallbackConverter.ReadAsPropertyNameCore(ref reader, typeToConvert, options);
}
internal virtual T ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
@@ -621,19 +616,14 @@ internal virtual T ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeTo
/// Method should be overridden in custom converters of types used in serialized dictionary keys.
public virtual void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
- if (!IsInternalConverter &&
- options.SerializerContext is null && // For consistency do not return any default converters for
- // options instances linked to a JsonSerializerContext,
- // even if the default converters might have been rooted.
- DefaultJsonTypeInfoResolver.TryGetDefaultSimpleConverter(TypeToConvert, out JsonConverter? defaultConverter))
+ // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
+ JsonConverter? fallbackConverter = GetFallbackConverterForPropertyNameSerialization(options);
+ if (fallbackConverter is null)
{
- // .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
- Debug.Assert(defaultConverter.IsInternalConverter && defaultConverter is JsonConverter);
- ((JsonConverter)defaultConverter).WriteAsPropertyNameCore(writer, value, options, isWritingExtensionDataProperty: false);
- return;
+ ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
}
- ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
+ fallbackConverter.WriteAsPropertyNameCore(writer, value, options, isWritingExtensionDataProperty: false);
}
internal virtual void WriteAsPropertyNameCore(Utf8JsonWriter writer, T value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
@@ -658,6 +648,29 @@ 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)
+ {
+ JsonConverter? result = null;
+
+ // For consistency do not return any default converters for options instances linked to a
+ // JsonSerializerContext, even if the default converters might have been rooted.
+ if (!IsInternalConverter && options.SerializerContext is null)
+ {
+ result = _fallbackConverterForPropertyNameSerialization;
+
+ if (result is null && DefaultJsonTypeInfoResolver.TryGetDefaultSimpleConverter(TypeToConvert, out JsonConverter? defaultConverter))
+ {
+ Debug.Assert(defaultConverter != this);
+ _fallbackConverterForPropertyNameSerialization = result = (JsonConverter)defaultConverter;
+ }
+ }
+
+ return result;
+ }
+
+ private JsonConverter? _fallbackConverterForPropertyNameSerialization;
+
internal virtual T ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options)
=> throw new InvalidOperationException();
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs
index 866690e86677ed..b6872d78575eeb 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs
@@ -246,6 +246,12 @@ public static void ThrowInvalidOperationException_ExpectedString(JsonTokenType t
throw GetInvalidOperationException("string", tokenType);
}
+ [DoesNotReturn]
+ public static void ThrowInvalidOperationException_ExpectedPropertyName(JsonTokenType tokenType)
+ {
+ throw GetInvalidOperationException("propertyName", tokenType);
+ }
+
[DoesNotReturn]
public static void ThrowInvalidOperationException_ExpectedStringComparison(JsonTokenType tokenType)
{
diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs
index 42f04dcd271ba7..5ca859459e8898 100644
--- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs
+++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.NonStringKey.cs
@@ -25,6 +25,18 @@ public async Task TestDictionaryKey(Dictionary dicti
Assert.Equal(dictionary, deserializedDictionary);
}
+ [Theory]
+ [MemberData(nameof(GetTestDictionaries))]
+ public async Task TestDictionaryKey_CustomConverter_ComposingWithDefaultConverter(Dictionary dictionary, string expectedJson)
+ {
+ var options = new JsonSerializerOptions { Converters = { new CustomPropertyNameConverter() } };
+ string json = await Serializer.SerializeWrapper(dictionary, options);
+ Assert.Equal(expectedJson, json);
+
+ Dictionary deserializedDictionary = await Serializer.DeserializeWrapper>(json, options);
+ Assert.Equal(dictionary, deserializedDictionary);
+ }
+
public static IEnumerable