0

The service I'm working with returns an empty array instead of null for objects. Which causes errors during deserialization.

System.Text.Json.JsonException : The JSON value could not be converted to Models.Error. Path: $.errors | LineNumber: 8 | BytePositionInLine: 13.

Sample #1:

{
  "data": {
    "code": 100,
    "message": "Success",
    "authority": "A00000000000000000000000000112233444",
    "fee_type": "Payer",
    "fee": 10
  },
  "errors": []
}

Sample #2:

{
  "data": [],
  "errors": {
    "code": -9,
    "message": "The input params invalid, validation error."
  }
}

This is what it came up with:

internal class InconsistentConverter<T> : JsonConverter<T>
{
    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            _ = reader.Read();
            return default;
        }

        return JsonSerializer.Deserialize<T>(ref reader, options);
    }

    public override void Write(Utf8JsonWriter writer, T objectToWrite, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

I want to use the converter for the data as well:

public class Result<T>
    where T : class
{
    [JsonPropertyName("data")]
    [JsonConverter(typeof(InconsistentConverter<T>))] // can't do this
    public T? Data { get; set; }

    [JsonPropertyName("errors")]
    [JsonConverter(typeof(InconsistentConverter<Error>))] // this works
    public Error? Error { get; set; }
}

I guess this would've been really easy in Json.NET but unfortunately I can't use it here.

Shleemypants
  • 2,081
  • 2
  • 14
  • 21
  • 1
    To support a strongly-typed generic converter, you'll need to use some reflection and implement a JsonConverterFactory. https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#steps-to-follow-the-factory-pattern – Cobster Nov 02 '21 at 23:59
  • @Charlieface Thanks, this service doesn't return single value at all, instead they return an empty array when there is no value. The provided solution may work but I think it's not necessary in this case. – Shleemypants Nov 04 '21 at 08:13
  • Sorry I think that's not right. It either returns an empty array, or it returns an object *not* in an array. So those solutions should work. You can modify them to return null for an empty array instead – Charlieface Nov 04 '21 at 10:52
  • Please kindly ignore my last comment since I wrote "single value" instead of "array". What I actually meant was that in my case the below solution works as expected and there is no need for the extended functionality. Maybe I should delete the question since it's really specific. – Shleemypants Nov 04 '21 at 11:35

1 Answers1

1

A quick workaround is to create a non-generic version of your converter:

internal class InconsistentConverter : JsonConverter<object>
{
    public override bool CanConvert(Type typeToConvert) => true;

    public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            _ = reader.Read();
            return default;
        }

        var deserialize = JsonSerializer.Deserialize(ref reader, typeToConvert, options);
        return deserialize;
    }

    public override void Write(Utf8JsonWriter writer, object objectToWrite, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

And use it for the generic property:

public class Result<T>
    where T : class
{
    [JsonPropertyName("data")]
    [JsonConverter(typeof(InconsistentConverter))]
    public T? Data { get; set; }

    [JsonPropertyName("errors")]
    [JsonConverter(typeof(InconsistentConverter<Error>))] // this works
    public Error? Error { get; set; }
}
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Thanks, I've tried this before but I get this: The converter specified on 'Models.Result`1[Models.Response].Data' is not compatible with the type 'Models.Response'. – Shleemypants Nov 03 '21 at 00:05
  • 1
    @PeymanM. please check my solution carefully. Have you overridden `CanConvert` as in the answer? – Guru Stron Nov 03 '21 at 00:07