Skip to content

JsonConverterAttribute can't be used to deserialize IEnumerable properties #31007

@GREsau

Description

@GREsau

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions