未验证 提交 c0662e81 编写于 作者: E Eirik Tsarpalis 提交者: GitHub

Fix regression in dictionary key serialization when registering custom...

Fix regression in dictionary key serialization when registering custom primitive converters. (#57138)

* Fixes regression in dictionary key serialization when registering custom primitive converters.

* reinstate debug assertion

* add test case for JsonTypeInfo overloads

* update method names

* fix sourcegen test
上级 8a60a83f
......@@ -55,7 +55,7 @@ internal abstract class DictionaryDefaultConverter<TDictionary, TKey, TValue>
{
state.Current.PropertyState = StackFramePropertyState.Name;
TKey key = enumerator.Current.Key;
_keyConverter.WriteWithQuotes(writer, key, options, ref state);
_keyConverter.WriteAsPropertyName(writer, key, options, ref state);
}
TValue element = enumerator.Current.Value;
......
......@@ -61,7 +61,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
do
{
TKey key = enumerator.Current.Key;
_keyConverter.WriteWithQuotes(writer, key, options, ref state);
_keyConverter.WriteAsPropertyName(writer, key, options, ref state);
_valueConverter.Write(writer, enumerator.Current.Value, options);
} while (enumerator.MoveNext());
}
......@@ -80,7 +80,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
state.Current.PropertyState = StackFramePropertyState.Name;
TKey key = enumerator.Current.Key;
_keyConverter.WriteWithQuotes(writer, key, options, ref state);
_keyConverter.WriteAsPropertyName(writer, key, options, ref state);
}
TValue element = enumerator.Current.Value;
......
......@@ -92,13 +92,13 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TDictionar
if (key is string keyString)
{
_keyConverter ??= GetConverter<string>(typeInfo.KeyTypeInfo!);
_keyConverter.WriteWithQuotes(writer, keyString, options, ref state);
_keyConverter.WriteAsPropertyName(writer, keyString, options, ref state);
}
else
{
// IDictionary is a special case since it has polymorphic object semantics on serialization
// but needs to use JsonConverter<string> on deserialization.
_valueConverter.WriteWithQuotes(writer, key, options, ref state);
_valueConverter.WriteAsPropertyName(writer, key, options, ref state);
}
}
......
......@@ -261,7 +261,7 @@ TKey ReadDictionaryKey(ref Utf8JsonReader reader, ref ReadStack state)
else
{
_keyConverter ??= GetConverter<TKey>(state.Current.JsonTypeInfo.KeyTypeInfo!);
key = _keyConverter.ReadWithQuotes(ref reader);
key = _keyConverter.ReadAsPropertyName(ref reader, typeToConvert, options);
unescapedPropertyNameAsString = reader.GetString()!;
}
......
......@@ -17,7 +17,7 @@ public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOpti
writer.WriteBooleanValue(value);
}
internal override bool ReadWithQuotes(ref Utf8JsonReader reader)
internal override bool ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
ReadOnlySpan<byte> propertyName = reader.GetSpan();
if (Utf8Parser.TryParse(propertyName, out bool value, out int bytesConsumed)
......@@ -29,7 +29,7 @@ internal override bool ReadWithQuotes(ref Utf8JsonReader reader)
throw ThrowHelper.GetFormatException(DataType.Boolean);
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, bool value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, bool value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -20,12 +20,12 @@ public override void Write(Utf8JsonWriter writer, byte value, JsonSerializerOpti
writer.WriteNumberValue(value);
}
internal override byte ReadWithQuotes(ref Utf8JsonReader reader)
internal override byte ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetByteWithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, byte value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, byte value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -28,10 +28,10 @@ public override void Write(Utf8JsonWriter writer, char value, JsonSerializerOpti
);
}
internal override char ReadWithQuotes(ref Utf8JsonReader reader)
=> Read(ref reader, default!, default!);
internal override char ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Read(ref reader, typeToConvert, options);
internal override void WriteWithQuotes(Utf8JsonWriter writer, char value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, char value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(
#if BUILDING_INBOX_LIBRARY
......
......@@ -15,12 +15,12 @@ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializer
writer.WriteStringValue(value);
}
internal override DateTime ReadWithQuotes(ref Utf8JsonReader reader)
internal override DateTime ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetDateTimeNoValidation();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -15,12 +15,12 @@ public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSeri
writer.WriteStringValue(value);
}
internal override DateTimeOffset ReadWithQuotes(ref Utf8JsonReader reader)
internal override DateTimeOffset ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetDateTimeOffsetNoValidation();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -20,12 +20,12 @@ public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerO
writer.WriteNumberValue(value);
}
internal override decimal ReadWithQuotes(ref Utf8JsonReader reader)
internal override decimal ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetDecimalWithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -20,12 +20,12 @@ public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOp
writer.WriteNumberValue(value);
}
internal override double ReadWithQuotes(ref Utf8JsonReader reader)
internal override double ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetDoubleWithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, double value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, double value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -84,7 +84,7 @@ public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerial
return default;
}
return ReadWithQuotes(ref reader);
return ReadAsPropertyName(ref reader, typeToConvert, options);
}
if (token != JsonTokenType.Number || !_converterOptions.HasFlag(EnumConverterOptions.AllowNumbers))
......@@ -304,7 +304,7 @@ private string FormatEnumValueToString(string value, JavaScriptEncoder? encoder)
return converted;
}
internal override T ReadWithQuotes(ref Utf8JsonReader reader)
internal override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string? enumString = reader.GetString();
......@@ -318,7 +318,7 @@ internal override T ReadWithQuotes(ref Utf8JsonReader reader)
return value;
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
{
// An EnumConverter that invokes this method
// can only be created by JsonSerializerOptions.GetDictionaryKeyConverter
......
......@@ -15,12 +15,12 @@ public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOpti
writer.WriteStringValue(value);
}
internal override Guid ReadWithQuotes(ref Utf8JsonReader reader)
internal override Guid ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetGuidNoValidation();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -21,12 +21,12 @@ public override void Write(Utf8JsonWriter writer, short value, JsonSerializerOpt
writer.WriteNumberValue((long)value);
}
internal override short ReadWithQuotes(ref Utf8JsonReader reader)
internal override short ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetInt16WithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, short value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, short value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -21,12 +21,12 @@ public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptio
writer.WriteNumberValue((long)value);
}
internal override int ReadWithQuotes(ref Utf8JsonReader reader)
internal override int ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetInt32WithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, int value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, int value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -20,12 +20,12 @@ public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOpti
writer.WriteNumberValue(value);
}
internal override long ReadWithQuotes(ref Utf8JsonReader reader)
internal override long ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetInt64WithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, long value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, long value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -30,13 +30,13 @@ public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerO
writer.WriteEndObject();
}
internal override object ReadWithQuotes(ref Utf8JsonReader reader)
internal override object ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
return null!;
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state)
{
// This converter does not handle nulls.
Debug.Assert(value != null);
......@@ -48,7 +48,7 @@ internal override void WriteWithQuotes(Utf8JsonWriter writer, object? value, Jso
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(runtimeType, this);
}
runtimeConverter.WriteWithQuotesAsObject(writer, value, options, ref state);
runtimeConverter.WriteAsPropertyNameAsObject(writer, value, options, ref state);
}
internal override object? ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options)
......
......@@ -20,12 +20,12 @@ public override void Write(Utf8JsonWriter writer, sbyte value, JsonSerializerOpt
writer.WriteNumberValue(value);
}
internal override sbyte ReadWithQuotes(ref Utf8JsonReader reader)
internal override sbyte ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetSByteWithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, sbyte value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, sbyte value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -21,12 +21,12 @@ public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOpt
writer.WriteNumberValue(value);
}
internal override float ReadWithQuotes(ref Utf8JsonReader reader)
internal override float ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetSingleWithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, float value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, float value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -23,12 +23,12 @@ public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerO
}
}
internal override string ReadWithQuotes(ref Utf8JsonReader reader)
internal override string ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetString()!;
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, string value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, string value, JsonSerializerOptions options, ref WriteStack state)
{
if (options.DictionaryKeyPolicy != null && !state.Current.IgnoreDictionaryKeyPolicy)
{
......
......@@ -21,12 +21,12 @@ public override void Write(Utf8JsonWriter writer, ushort value, JsonSerializerOp
writer.WriteNumberValue((long)value);
}
internal override ushort ReadWithQuotes(ref Utf8JsonReader reader)
internal override ushort ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetUInt16WithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, ushort value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, ushort value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -21,12 +21,12 @@ public override void Write(Utf8JsonWriter writer, uint value, JsonSerializerOpti
writer.WriteNumberValue((ulong)value);
}
internal override uint ReadWithQuotes(ref Utf8JsonReader reader)
internal override uint ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetUInt32WithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, uint value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, uint value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -20,12 +20,12 @@ public override void Write(Utf8JsonWriter writer, ulong value, JsonSerializerOpt
writer.WriteNumberValue(value);
}
internal override ulong ReadWithQuotes(ref Utf8JsonReader reader)
internal override ulong ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.GetUInt64WithQuotes();
}
internal override void WriteWithQuotes(Utf8JsonWriter writer, ulong value, JsonSerializerOptions options, ref WriteStack state)
internal override void WriteAsPropertyName(Utf8JsonWriter writer, ulong value, JsonSerializerOptions options, ref WriteStack state)
{
writer.WritePropertyName(value);
}
......
......@@ -105,9 +105,9 @@ internal bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state)
internal abstract bool WriteCoreAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state);
/// <summary>
/// Loosely-typed WriteWithQuotes() that forwards to strongly-typed WriteWithQuotes().
/// Loosely-typed WriteToPropertyName() that forwards to strongly-typed WriteToPropertyName().
/// </summary>
internal abstract void WriteWithQuotesAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state);
internal abstract void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state);
// Whether a type (ConverterStrategy.Object) is deserialized using a parameterized constructor.
internal virtual bool ConstructorIsParameterized { get; }
......
......@@ -119,7 +119,7 @@ internal JsonConverter GetConverterInternal(Type typeToConvert, JsonSerializerOp
throw new InvalidOperationException();
}
internal sealed override void WriteWithQuotesAsObject(
internal sealed override void WriteAsPropertyNameAsObject(
Utf8JsonWriter writer, object value,
JsonSerializerOptions options,
ref WriteStack state)
......
......@@ -607,17 +607,34 @@ internal void VerifyWrite(int originalDepth, Utf8JsonWriter writer)
#nullable restore
JsonSerializerOptions options);
internal virtual T ReadWithQuotes(ref Utf8JsonReader reader)
internal virtual T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (!IsInternalConverter && options.TryGetDefaultSimpleConverter(TypeToConvert, out JsonConverter? defaultConverter))
{
// .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
Debug.Assert(defaultConverter.IsInternalConverter && defaultConverter is JsonConverter<T>);
return ((JsonConverter<T>)defaultConverter).ReadAsPropertyName(ref reader, TypeToConvert, options);
}
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
return default;
}
internal virtual void WriteWithQuotes(Utf8JsonWriter writer, [DisallowNull] T value, JsonSerializerOptions options, ref WriteStack state)
=> ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
internal virtual void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
{
if (!IsInternalConverter && options.TryGetDefaultSimpleConverter(TypeToConvert, out JsonConverter? defaultConverter))
{
// .NET 5 backward compatibility: hardcode the default converter for primitive key serialization.
Debug.Assert(defaultConverter.IsInternalConverter && defaultConverter is JsonConverter<T>);
((JsonConverter<T>)defaultConverter).WriteAsPropertyName(writer, value, options, ref state);
return;
}
ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(TypeToConvert, this);
}
internal sealed override void WriteWithQuotesAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state)
=> WriteWithQuotes(writer, (T)value, options, ref state);
internal sealed override void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state)
=> WriteAsPropertyName(writer, (T)value, options, ref state);
internal virtual T ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options)
=> throw new InvalidOperationException();
......
......@@ -224,7 +224,6 @@ internal JsonConverter GetConverterInternal(Type typeToConvert)
if (s_defaultSimpleConverters.TryGetValue(typeToConvert, out JsonConverter? foundConverter))
{
Debug.Assert(foundConverter != null);
converter = foundConverter;
}
else
......@@ -318,6 +317,21 @@ private JsonConverter GetConverterFromAttribute(JsonConverterAttribute converter
return converter;
}
internal bool TryGetDefaultSimpleConverter(Type typeToConvert, [NotNullWhen(true)] out JsonConverter? converter)
{
if (_context == 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.
s_defaultSimpleConverters != null &&
s_defaultSimpleConverters.TryGetValue(typeToConvert, out converter))
{
return true;
}
converter = null;
return false;
}
private static Attribute? GetAttributeThatCanHaveMultiple(Type classType, Type attributeType, MemberInfo memberInfo)
{
object[] attributes = memberInfo.GetCustomAttributes(attributeType, inherit: false);
......
......@@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.IO;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
using Xunit;
......@@ -621,59 +622,93 @@ void RunTest<T>(T dictionary)
}
[Fact]
public static void KeyWithCustomConverter()
public static void KeyWithCustomPrimitiveConverter_FallbackToDefaultConverter()
{
// TODO: update these tests after https://github.com/dotnet/runtime/issues/50071 is implemented.
// Validates .NET 5 primitive custom key converter behavior.
JsonSerializerOptions options = new()
{
Converters = { new ConverterForInt32(), new ComplexKeyConverter() }
Converters = { new ConverterForInt32() }
};
// Primitive key
string json = @"{
""PrimitiveKey"":{
""1"":""1""
}
}
";
ClassWithNonStringDictKeys obj = new()
{
PrimitiveKey = new Dictionary<int, string> { [1] = "1" },
};
RunTest(obj, json, typeof(int).ToString(), typeof(ConverterForInt32).ToString());
var dictionary = new Dictionary<int, string> { [1] = "1" };
// Complex key
json = @"{
""ComplexKey"":{
""SomeStringRepresentation"":""1""
}
}
";
obj = new()
string expectedJson = @"{""1"":""1""}";
string actualJson = JsonSerializer.Serialize(dictionary, options);
Assert.Equal(expectedJson, actualJson);
dictionary = JsonSerializer.Deserialize<Dictionary<int, string>>(expectedJson);
Assert.True(dictionary.ContainsKey(1));
}
[Fact]
public static void KeyWithCustomPrimitiveConverter_JsonTypeInfo_ThrowsNotSupportedException()
{
JsonSerializer.Serialize(42); // Ensure default converters are rooted in current process
CustomInt32ConverterSerializerContext ctx = new();
var dictionary = new Dictionary<int, string> { [1] = "1" };
NotSupportedException ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Serialize(dictionary, ctx.DictionaryInt32String));
ValidateException(ex);
string json = @"{""1"":""1""}";
ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Deserialize<Dictionary<int, string>>(json, ctx.DictionaryInt32String));
ValidateException(ex);
static void ValidateException(NotSupportedException ex)
{
ComplexKey = new Dictionary<ClassWithIDictionary, string> { [new ClassWithIDictionary()] = "1" },
};
RunTest(obj, json, typeof(ClassWithIDictionary).ToString(), typeof(ComplexKeyConverter).ToString());
Assert.Contains(nameof(Int32), ex.Message);
Assert.Contains(nameof(ConverterForInt32), ex.Message);
}
}
public class CustomInt32ConverterSerializerContext : JsonSerializerContext
{
public CustomInt32ConverterSerializerContext() : base(null, null) { }
public override JsonTypeInfo? GetTypeInfo(Type _) => throw new NotImplementedException();
void RunTest(ClassWithNonStringDictKeys obj, string payload, string keyTypeAsStr, string converterTypeAsStr)
public JsonTypeInfo<Dictionary<int, string>> DictionaryInt32String => _dictionaryInt32String ??= CreateDictionaryConverter();
private JsonTypeInfo<Dictionary<int, string>>? _dictionaryInt32String;
private JsonTypeInfo<Dictionary<int, string>> CreateDictionaryConverter()
{
NotSupportedException ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Serialize(obj, options));
string exAsStr = ex.ToString();
Assert.Contains(keyTypeAsStr, exAsStr);
Assert.Contains(converterTypeAsStr, exAsStr);
ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Deserialize<ClassWithNonStringDictKeys>(payload, options));
exAsStr = ex.ToString();
Assert.Contains(keyTypeAsStr, exAsStr);
Assert.Contains(converterTypeAsStr, exAsStr);
JsonTypeInfo<int> keyInfo = JsonMetadataServices.CreateValueInfo<int>(Options, new ConverterForInt32());
JsonTypeInfo<string> valueInfo = JsonMetadataServices.CreateValueInfo<string>(Options, JsonMetadataServices.StringConverter);
return JsonMetadataServices.CreateDictionaryInfo<Dictionary<int, string>, int, string>(
Options,
createObjectFunc: () => new(),
keyInfo, valueInfo,
numberHandling: default,
serializeFunc: null
);
}
}
private class ClassWithNonStringDictKeys
[Fact]
public static void KeyWithCustomClassConverter_ThrowsNotSupportedException()
{
public Dictionary<int, string> PrimitiveKey { get; set; }
public Dictionary<ClassWithIDictionary, string> ComplexKey { get; set; }
// TODO: update after https://github.com/dotnet/runtime/issues/46520 is implemented.
JsonSerializerOptions options = new()
{
Converters = { new ComplexKeyConverter() }
};
var dictionary = new Dictionary<ClassWithIDictionary, string> { [new ClassWithIDictionary()] = "1" };
NotSupportedException ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Serialize(dictionary, options));
ValidateException(ex);
string json = @"{""SomeStringRepresentation"":""1""}";
ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Deserialize<Dictionary<ClassWithIDictionary, string>>(json, options));
ValidateException(ex);
static void ValidateException(NotSupportedException ex)
{
Assert.Contains(nameof(ClassWithIDictionary), ex.Message);
Assert.Contains(nameof(ComplexKeyConverter), ex.Message);
}
}
private class ComplexKeyConverter : JsonConverter<ClassWithIDictionary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册