Search code examples
c#jsonsystem.text.json

System.Text.Json: Error while deserializing dictionary returned as empty array


I can't seem to solve this one. All other questions i can find with the same issue are for Newtonsoft.Json.

I use an external provided json API which returns a dictionary. But if it's empty, they return an empty array:

With data:

"booking/index": {
   "key": {
         data...
          }
}

Without data:

"booking/index": []

So the System.json deserializer fails with this error (which is understandable):

System.Text.Json.JsonException: The JSON value could not be converted to System.Collections.Generic.Dictionary`2[System.String,checkfront_reports.Models.Api.BookingIndexIndexObj]. Path: $['booking/index'] | LineNumber: 0 | BytePositionInLine: 408.

For reference here is the call i make and the data model:

var bookingIndex = await httpClient.GetFromJsonAsync<BookingIndexObj>($"booking/index?start_date={startDate.ToString("yyyy-MM-dd")}&end_date={endDate.ToString("yyyy-MM-dd")}");

public class BookingIndexObj
{
    public BookingRequestObj Request { get; set; }

    [JsonPropertyName("booking/index")]
    public Dictionary<string, BookingIndexIndexObj> BookingIndex { get; set; }
}

public class BookingIndexIndexObj
{
    public string? Code { get; set; }

    [JsonPropertyName("status_id")]
    public string? StatusId { get; set; }
}

public class BookingRequestObj
{
    public int Page { get; set; }

    public int Pages { get; set; }
}

Solution

  • I am not sure at all if this is how we are supposed to do this, but at least it worked:

    (Note that I simplified the example)

    using System;
    using System.Text.Json;
    using System.Text.Json.Serialization;
    using System.Collections.Generic;
                        
    public class Program
    {
        public static void Main()
        {
            var emptyJson = 
                """
    {
        "values": []
    }
    """;
        
            var nonEmptyJson = 
                """
    {
        "values": {
            "key1": "value1",
            "key2": "value2"
        }
    }
    """;
            
            var emptyResult = JsonSerializer.Deserialize<Model>(emptyJson);
            var nonEmptyResult = JsonSerializer.Deserialize<Model>(nonEmptyJson);
            
            emptyResult.Dump();
            nonEmptyResult.Dump();
        }
    }
    
    public class Model
    {
        [JsonPropertyName("values")]
        [JsonConverter(typeof(MySpecialConverter))]
        public Dictionary<string,string> Values {get;set;} = new();
    }
    
    public class MySpecialConverter : JsonConverter<Dictionary<string,string>>
    {
    // Omitted writer for brevity. 
        public override void Write(Utf8JsonWriter writer,
                Dictionary<string,string> dictionaryValue,
                JsonSerializerOptions options) => throw new NotSupportedException();
                
        public override Dictionary<string,string> Read(ref Utf8JsonReader reader,
                Type typeToConvert,
                JsonSerializerOptions options)
                {
    // Check for '"booking/index": []' - case:
                    if (reader.TokenType == JsonTokenType.StartArray)
                    {
                      reader.Read();
                      return new Dictionary<string,string>();
                    }
    // Otherwise pass-through to default converter.
                    var converter = (JsonConverter<Dictionary<string,string>>)options.GetConverter(typeof(Dictionary<string,string>));
                    return converter.Read(ref reader, typeToConvert, options);
                }
    }
    

    => Fiddle: https://dotnetfiddle.net/J6NbVE

    For your reference:
    I based this upon this article: How to write custom converters for JSON serialization (marshalling) in .NET