-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
When trying to deserialize an object where one of the properties is an IEnumerable/collection and has a custom converter set by JsonConverterAttribute, a JsonException is thrown. Repro:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace JsonConverterRepro
{
class Program
{
static void Main(string[] args)
{
var options = new JsonSerializerOptions();
// Program works as expected when this line is uncommented:
// options.Converters.Add(new DummyConverter());
var json = @"{ ""Items"": [""hello"", 1, true] }";
var model = JsonSerializer.Deserialize<MyModel>(json, options);
Console.WriteLine(string.Join(", ", model.Items));
}
class MyModel
{
[JsonConverter(typeof(ToStringConverter))]
public List<string> Items { get; set; }
}
// This converter coerces list items into strings when deserializing
class ToStringConverter : JsonConverter<List<string>>
{
public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var doc = JsonDocument.ParseValue(ref reader);
return doc.RootElement.EnumerateArray().Select(e => e.ToString()).ToList();
}
public override void Write(Utf8JsonWriter writer, List<string> value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}
class DummyConverter : JsonConverter<List<string>>
{
public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
throw new NotImplementedException();
public override void Write(Utf8JsonWriter writer, List<string> value, JsonSerializerOptions options) =>
throw new NotImplementedException();
}
}
}This program should output hello, 1, True. However, when this is actually run, this exception is thrown:
Unhandled exception. System.Text.Json.JsonException: The JSON value could not be converted to System.Collections.Generic.List`1[System.String]. Path: $.Items | LineNumber: 0 | BytePositionInLine: 12.
---> System.InvalidOperationException: Cannot get the value of a token type 'StartArray' as a string.
at System.Text.Json.Utf8JsonReader.GetString()
at System.Text.Json.Serialization.Converters.JsonConverterString.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.JsonPropertyInfoNotNullable`4.OnReadEnumerable(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.JsonPropertyInfo.ReadEnumerable(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.JsonPropertyInfo.Read(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.JsonSerializer.HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& readStack, Utf8JsonReader& reader, Exception ex)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadCore(Type returnType, JsonSerializerOptions options, Utf8JsonReader& reader)
at System.Text.Json.JsonSerializer.ParseCore(String json, Type returnType, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)
at JsonConverterRepro.Program.Main(String[] args) in H:\projects\JsonConverterRepro\Program.cs:line 17
The correct behaviour is observed when when a custom converter for that property's type is added to the JsonSerializerOptions, even if that custom converter doesn't get used. This program also works correctly when the ToStringConverter is added to the JsonSerializerOptions instead of being set via an attribute.
Although this repro uses List<string>, the same problem occurs with other collection types and item types e.g. IEnumerable<string>, HashSet<int>.
Tested on .NET Core 3.0.100