public abstract Type GetConcreteType(Type type);
- private static void GetOriginalValues(ref Utf8JsonReader reader, out JsonTokenType tokenType, out int depth)
+ private static void GetOriginalValues(ref Utf8JsonReader reader, out JsonTokenType tokenType, out int depth, out long bytesConsumed)
{
tokenType = reader.TokenType;
depth = reader.CurrentDepth;
+ bytesConsumed = reader.BytesConsumed;
}
public virtual void GetPolicies()
{
// Forward the setter to the value-based JsonPropertyInfo.
JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
- propertyInfo.OnReadEnumerable(tokenType, ref state, ref reader);
+ propertyInfo.ReadEnumerable(tokenType, ref state, ref reader);
}
else
{
- GetOriginalValues(ref reader, out JsonTokenType originalTokenType, out int originalDepth);
+ GetOriginalValues(ref reader, out JsonTokenType originalTokenType, out int originalDepth, out long bytesConsumed);
OnRead(tokenType, ref state, ref reader);
- VerifyRead(originalTokenType, originalDepth, ref state, ref reader);
+ VerifyRead(originalTokenType, originalDepth, bytesConsumed, ref state, ref reader);
}
}
{
Debug.Assert(ShouldDeserialize);
- GetOriginalValues(ref reader, out JsonTokenType originalTokenType, out int originalDepth);
+ GetOriginalValues(ref reader, out JsonTokenType originalTokenType, out int originalDepth, out long bytesConsumed);
OnReadEnumerable(tokenType, ref state, ref reader);
- VerifyRead(originalTokenType, originalDepth, ref state, ref reader);
+ VerifyRead(originalTokenType, originalDepth, bytesConsumed, ref state, ref reader);
}
public JsonClassInfo RuntimeClassInfo
public bool ShouldSerialize { get; private set; }
public bool ShouldDeserialize { get; private set; }
- private void VerifyRead(JsonTokenType originalTokenType, int originalDepth, ref ReadStack state, ref Utf8JsonReader reader)
+ private void VerifyRead(JsonTokenType originalTokenType, int originalDepth, long bytesConsumed, ref ReadStack state, ref Utf8JsonReader reader)
{
- // We don't have a single call to ThrowHelper since the line number captured during throw may be useful for diagnostics.
switch (originalTokenType)
{
case JsonTokenType.StartArray:
default:
// Reading a single property value.
- if (reader.TokenType != originalTokenType)
+ if (reader.TokenType != originalTokenType || reader.BytesConsumed != bytesConsumed)
{
- // todo issue #38550 blocking this: originalDepth != reader.CurrentDepth + 1
ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString());
}
{
// Forward the setter to the value-based JsonPropertyInfo.
JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
- propertyInfo.OnWriteEnumerable(ref state.Current, writer);
+ propertyInfo.WriteEnumerable(ref state, writer);
}
else
{
Debug.Assert(state.Current.IsProcessingEnumerableOrDictionary);
- if (state.Current.PropertyInitialized)
+ if (state.Current.CollectionPropertyInitialized)
{
// A nested json array so push a new stack frame.
Type elementType = jsonPropertyInfo.ElementClassInfo.Type;
state.Push();
state.Current.Initialize(elementType, options);
- state.Current.PropertyInitialized = true;
+ state.Current.CollectionPropertyInitialized = true;
}
else
{
- state.Current.PropertyInitialized = true;
+ state.Current.CollectionPropertyInitialized = true;
}
jsonPropertyInfo = state.Current.JsonPropertyInfo;
Debug.Assert(jsonPropertyInfo != null);
// A nested object or dictionary so push new frame.
- if (state.Current.PropertyInitialized)
+ if (state.Current.CollectionPropertyInitialized)
{
state.Push();
state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo;
state.Current.InitializeJsonPropertyInfo();
- state.Current.PropertyInitialized = true;
+ state.Current.CollectionPropertyInitialized = true;
ClassType classType = state.Current.JsonClassInfo.ClassType;
if (classType == ClassType.Value &&
return;
}
- state.Current.PropertyInitialized = true;
+ state.Current.CollectionPropertyInitialized = true;
if (state.Current.IsProcessingIDictionaryConstructible)
{
if (state.Current.IsEnumerableProperty || state.Current.IsDictionaryProperty || state.Current.IsIDictionaryConstructibleProperty)
{
- bool setPropertyToNull = !state.Current.PropertyInitialized;
+ bool setPropertyToNull = !state.Current.CollectionPropertyInitialized;
ApplyObjectToEnumerable(null, ref state, ref reader, setPropertyDirectly: setPropertyToNull);
return false;
}
{
state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject();
}
- else if (state.Current.JsonClassInfo.ClassType == ClassType.Value)
- {
- // Custom converter.
- state.Current.JsonPropertyInfo.Read(JsonTokenType.StartObject, ref state, ref reader);
- HandleEndObject(options, ref reader, ref state);
- }
else
{
state.Current.ReturnValue = classInfo.CreateObject();
public IList TempEnumerableValues;
// Has an array or dictionary property been initialized.
- public bool PropertyInitialized;
+ public bool CollectionPropertyInitialized;
// Support IDictionary constructible types, i.e. types that we
// support by passing and IDictionary to their constructors:
// The current JSON data for a property does not match a given POCO, so ignore the property (recursively).
public bool Drain;
+ public bool IsCollectionForClass => IsEnumerable || IsDictionary || IsIDictionaryConstructible;
+ public bool IsCollectionForProperty => IsEnumerableProperty || IsDictionaryProperty || IsIDictionaryConstructibleProperty;
+
public bool IsIDictionaryConstructible => JsonClassInfo.ClassType == ClassType.IDictionaryConstructible;
public bool IsDictionary => JsonClassInfo.ClassType == ClassType.Dictionary;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- if (JsonPropertyInfo == null || SkipProperty)
+ if (SkipProperty)
{
return false;
}
- if (PropertyInitialized)
+ if (CollectionPropertyInitialized)
{
- if ((IsEnumerable || IsDictionary || IsIDictionaryConstructible) &&
- JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown)
+ ClassType elementType;
+
+ if (IsCollectionForProperty)
{
- return true;
+ elementType = JsonPropertyInfo.ElementClassInfo.ClassType;
}
- else if ((IsEnumerableProperty || IsDictionaryProperty || IsIDictionaryConstructibleProperty) &&
- JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown)
+ else
{
- return true;
+ Debug.Assert(IsCollectionForClass);
+ elementType = JsonClassInfo.ElementClassInfo.ClassType;
}
+
+ return (elementType == ClassType.Value || elementType == ClassType.Unknown);
+ }
+
+ ClassType type;
+ if (JsonPropertyInfo == null)
+ {
+ type = JsonClassInfo.ClassType;
+ }
+ else
+ {
+ type = JsonPropertyInfo.ClassType;
}
- // We've got a property info. If we're a Value or polymorphic Value
- // (ClassType.Unknown), return true.
- ClassType type = JsonPropertyInfo.ClassType;
- return type == ClassType.Value || type == ClassType.Unknown ||
- KeyName != null && (
- (IsDictionary && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
- (IsDictionaryProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
- (IsIDictionaryConstructible && JsonClassInfo.ElementClassInfo.ClassType == ClassType.Unknown) ||
- (IsIDictionaryConstructibleProperty && JsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Unknown)
- );
+ // If we're a Value or polymorphic Value (ClassType.Unknown), return true.
+ return type == ClassType.Value || type == ClassType.Unknown;
}
}
public void ResetProperty()
{
- PropertyInitialized = false;
+ CollectionPropertyInitialized = false;
JsonPropertyInfo = null;
TempEnumerableValues = null;
TempDictionaryValues = null;
public Type GetElementType()
{
- if (IsEnumerableProperty || IsDictionaryProperty || IsIDictionaryConstructibleProperty)
+ if (IsCollectionForProperty)
{
return JsonPropertyInfo.ElementClassInfo.Type;
}
- if (IsEnumerable || IsDictionary || IsIDictionaryConstructible)
+ if (IsCollectionForClass)
{
return JsonClassInfo.ElementClassInfo.Type;
}
}
catch (JsonException ex)
{
- Assert.Contains("$.Level2.Level3s[0]", ex.ToString());
- Assert.Equal(ex.Path, "$.Level2.Level3s[0]");
+ Assert.Contains("$.Level2.Level3s[1]", ex.ToString());
+ Assert.Equal(ex.Path, "$.Level2.Level3s[1]");
}
}
/// <summary>
/// A converter that uses Object as it's type.
/// </summary>
- private class ObjectConverter : JsonConverter<object>
+ private class ObjectToCustomerOrIntConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert)
{
public static void CustomObjectConverter()
{
var options = new JsonSerializerOptions();
- options.Converters.Add(new ObjectConverter());
+ options.Converters.Add(new ObjectToCustomerOrIntConverter());
{
var customer = new Customer();
}
{
- Customer obj = JsonSerializer.Parse<Customer>("{}", options);
- Assert.Equal("HelloWorld", obj.Name);
+ object obj = JsonSerializer.Parse<Customer>("{}", options);
+ Assert.IsType<Customer>(obj);
+ Assert.Equal("HelloWorld", ((Customer)obj).Name);
+ }
+
+ {
+ // The converter doesn't handle object.
+ object obj = JsonSerializer.Parse<object>("{}", options);
+ Assert.IsType<JsonElement>(obj);
}
{
Assert.Equal(42, obj);
}
}
+
+ /// <summary>
+ /// A converter that converters True and False to a bool.
+ /// </summary>
+ private class ObjectToBoolConverter : JsonConverter<object>
+ {
+ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.True)
+ {
+ return true;
+ }
+
+ if (reader.TokenType == JsonTokenType.False)
+ {
+ return false;
+ }
+
+ // Use JsonElement as fallback.
+ var converter = options.GetConverter(typeof(JsonElement)) as JsonConverter<JsonElement>;
+ if (converter != null)
+ {
+ return converter.Read(ref reader, typeToConvert, options);
+ }
+
+ // Shouldn't get here.
+ throw new JsonException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
+ {
+ throw new InvalidOperationException("Directly writing object not supported");
+ }
+
+ public override void Write(Utf8JsonWriter writer, object value, JsonEncodedText propertyName, JsonSerializerOptions options)
+ {
+ throw new InvalidOperationException("Directly writing object not supported");
+ }
+ }
+
+ [Fact]
+ public static void CustomObjectBoolConverter()
+ {
+ var options = new JsonSerializerOptions();
+ options.Converters.Add(new ObjectToBoolConverter());
+
+ {
+ object obj = JsonSerializer.Parse<object>("true", options);
+ Assert.IsType<bool>(obj);
+ Assert.True((bool)obj);
+ }
+
+ {
+ object obj = JsonSerializer.Parse<object>("false", options);
+ Assert.IsType<bool>(obj);
+ Assert.False((bool)obj);
+ }
+
+ {
+ object obj = JsonSerializer.Parse<object>("{}", options);
+ Assert.IsType<JsonElement>(obj);
+ }
+ }
}
}
JsonSerializer.Parse<EmptyClass>(SimpleTestClassWithNulls.s_json);
}
- [Fact]
- public static void ReadObjectFail()
- {
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>("blah"));
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<object>("blah"));
-
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>("true"));
-
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>("null."));
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<object>("null."));
+ [Theory]
+ [InlineData("blah", true)]
+ [InlineData("null.", true)]
+ [InlineData("{{}", true)]
+ [InlineData("{", true)]
+ [InlineData("}", true)]
+ [InlineData("", true)]
+ [InlineData("true", false)]
+ [InlineData("[]", false)]
+ [InlineData("[{}]", false)]
+ public static void ReadObjectFail(string json, bool failsOnObject)
+ {
+ Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>(json));
+
+ if (failsOnObject)
+ {
+ Assert.Throws<JsonException>(() => JsonSerializer.Parse<object>(json));
+ }
+ else
+ {
+ Assert.IsType<JsonElement>(JsonSerializer.Parse<object>(json));
+ }
}
[Fact]
}
[Fact]
- public static void ParseUntyped()
- {
- // Not supported until we are able to deserialize into JsonElement.
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<SimpleTestClass>("[]"));
- Assert.Throws<JsonException>(() => JsonSerializer.Parse<object>("[]"));
- }
-
- [Fact]
public static void ReadClassWithStringToPrimitiveDictionary()
{
TestClassWithStringToPrimitiveDictionary obj = JsonSerializer.Parse<TestClassWithStringToPrimitiveDictionary>(TestClassWithStringToPrimitiveDictionary.s_data);
obj = JsonSerializer.Parse<object>(@"null");
Assert.Null(obj);
+
+ obj = JsonSerializer.Parse<object>(@"[]");
+ element = (JsonElement)obj;
+ Assert.Equal(JsonValueType.Array, element.Type);
}
[Fact]