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

Fix error reporting for objects with unsupported property types (#67109)

* Fix error reporting for objects with unsupported property types

* refactor dictionary tests to use xunit theories

* lift test class to abstracted test harness
上级 66271834
......@@ -83,6 +83,7 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
ref ReadStack state,
[MaybeNullWhen(false)] out TDictionary value)
{
JsonTypeInfo keyTypeInfo = state.Current.JsonTypeInfo.KeyTypeInfo!;
JsonTypeInfo elementTypeInfo = state.Current.JsonTypeInfo.ElementTypeInfo!;
if (state.UseFastPath)
......@@ -96,8 +97,9 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
CreateCollection(ref reader, ref state);
state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
_keyConverter ??= GetConverter<TKey>(keyTypeInfo);
_valueConverter ??= GetConverter<TValue>(elementTypeInfo);
if (_valueConverter.CanUseDirectReadOrWrite && state.Current.NumberHandling == null)
{
// Process all elements.
......@@ -114,10 +116,12 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
// Read method would have thrown if otherwise.
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
TKey key = ReadDictionaryKey(ref reader, ref state);
state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
TKey key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
// Read the value and add.
reader.ReadWithVerify();
state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
TValue? element = _valueConverter.Read(ref reader, ElementType, options);
Add(key, element!, options, ref state);
}
......@@ -137,12 +141,13 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
// Read method would have thrown if otherwise.
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
TKey key = ReadDictionaryKey(ref reader, ref state);
state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
TKey key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
reader.ReadWithVerify();
// Get the value from the converter and add it.
state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
_valueConverter.TryRead(ref reader, ElementType, options, ref state, out TValue? element);
Add(key, element!, options, ref state);
}
......@@ -160,7 +165,6 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
}
state.Current.ObjectState = StackFrameObjectState.StartToken;
state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
}
// Handle the metadata properties.
......@@ -191,6 +195,7 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
}
// Process all elements.
_keyConverter ??= GetConverter<TKey>(keyTypeInfo);
_valueConverter ??= GetConverter<TValue>(elementTypeInfo);
while (true)
{
......@@ -229,7 +234,8 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
}
}
key = ReadDictionaryKey(ref reader, ref state);
state.Current.JsonPropertyInfo = keyTypeInfo.PropertyInfoForTypeInfo;
key = ReadDictionaryKey(_keyConverter, ref reader, ref state, options);
}
else
{
......@@ -252,6 +258,7 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
if (state.Current.PropertyState < StackFramePropertyState.TryRead)
{
// Get the value from the converter and add it.
state.Current.JsonPropertyInfo = elementTypeInfo.PropertyInfoForTypeInfo;
bool success = _valueConverter.TryRead(ref reader, typeof(TValue), options, ref state, out TValue? element);
if (!success)
{
......@@ -270,27 +277,22 @@ protected static JsonConverter<T> GetConverter<T>(JsonTypeInfo typeInfo)
value = (TDictionary)state.Current.ReturnValue!;
return true;
TKey ReadDictionaryKey(ref Utf8JsonReader reader, ref ReadStack state)
static TKey ReadDictionaryKey(JsonConverter<TKey> keyConverter, ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
{
TKey key;
string unescapedPropertyNameAsString;
_keyConverter ??= GetConverter<TKey>(state.Current.JsonTypeInfo.KeyTypeInfo!);
string unescapedPropertyNameAsString = reader.GetString()!;
state.Current.JsonPropertyNameAsString = unescapedPropertyNameAsString; // Copy key name for JSON Path support in case of error.
// Special case string to avoid calling GetString twice and save one allocation.
if (_keyConverter.IsInternalConverter && KeyType == typeof(string))
if (keyConverter.IsInternalConverter && keyConverter.TypeToConvert == typeof(string))
{
unescapedPropertyNameAsString = reader.GetString()!;
key = (TKey)(object)unescapedPropertyNameAsString;
}
else
{
key = _keyConverter.ReadAsPropertyNameCore(ref reader, KeyType, options);
unescapedPropertyNameAsString = reader.GetString()!;
key = keyConverter.ReadAsPropertyNameCore(ref reader, keyConverter.TypeToConvert, options);
}
// Copy key name for JSON Path support in case of error.
state.Current.JsonPropertyNameAsString = unescapedPropertyNameAsString;
return key;
}
}
......
......@@ -290,7 +290,7 @@ public static void AddJsonExceptionInformation(ref ReadStack state, in Utf8JsonR
if (string.IsNullOrEmpty(message))
{
// Use a default message.
Type propertyType = state.Current.JsonTypeInfo.Type;
Type propertyType = state.Current.JsonPropertyInfo?.PropertyType ?? state.Current.JsonTypeInfo.Type;
message = SR.Format(SR.DeserializeUnableToConvertValue, propertyType);
ex.AppendPathInformation = true;
}
......@@ -371,7 +371,7 @@ public static void ThrowNotSupportedException(ref ReadStack state, in Utf8JsonRe
Debug.Assert(!message.Contains(" Path: "));
// Obtain the type to show in the message.
Type propertyType = state.Current.JsonTypeInfo.Type;
Type propertyType = state.Current.JsonPropertyInfo?.PropertyType ?? state.Current.JsonTypeInfo.Type;
if (!message.Contains(propertyType.ToString()))
{
......@@ -399,7 +399,7 @@ public static void ThrowNotSupportedException(ref WriteStack state, NotSupported
Debug.Assert(!message.Contains(" Path: "));
// Obtain the type to show in the message.
Type propertyType = state.Current.JsonTypeInfo.Type;
Type propertyType = state.Current.JsonPropertyInfo?.PropertyType ?? state.Current.JsonTypeInfo.Type;
if (!message.Contains(propertyType.ToString()))
{
......
......@@ -133,7 +133,7 @@ public static void ConverterWithReentryFail()
ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Deserialize<TopLevelPocoWithNoConverter>(Json, options));
Assert.Contains(typeof(int[,]).ToString(), ex.ToString());
Assert.Contains(typeof(ChildPocoWithNoConverter).ToString(), ex.ToString());
Assert.Contains(typeof(ChildPocoWithNoConverterAndInvalidProperty).ToString(), ex.ToString());
Assert.Contains("Path: $.InvalidProperty | LineNumber: 0 | BytePositionInLine: 20.", ex.ToString());
Assert.Equal(2, ex.ToString().Split(new string[] { "Path:" }, StringSplitOptions.None).Length);
......@@ -153,7 +153,7 @@ public static void ConverterWithReentryFail()
ex = Assert.Throws<NotSupportedException>(() => JsonSerializer.Serialize(poco, options));
Assert.Contains(typeof(int[,]).ToString(), ex.ToString());
Assert.Contains(typeof(ChildPocoWithNoConverter).ToString(), ex.ToString());
Assert.Contains(typeof(ChildPocoWithNoConverterAndInvalidProperty).ToString(), ex.ToString());
Assert.Contains("Path: $.InvalidProperty.", ex.ToString());
Assert.Equal(2, ex.ToString().Split(new string[] { "Path:" }, StringSplitOptions.None).Length);
}
......
......@@ -458,7 +458,7 @@ public static void ClassWithUnsupportedArrayInProperty()
JsonSerializer.Serialize(new ClassWithPropertyToClassWithInvalidArray()));
Assert.Contains(typeof(int[,]).ToString(), ex.Message);
Assert.Contains(typeof(ClassWithPropertyToClassWithInvalidArray).ToString(), ex.Message);
Assert.Contains(typeof(ClassWithInvalidArray).ToString(), ex.Message);
Assert.Contains("Path: $.Inner.", ex.Message);
// The original exception contains the type.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册