Home > OS >  Getting JsonException "Read too much or not enough" for my JsonConverter
Getting JsonException "Read too much or not enough" for my JsonConverter

Time:10-14

I am receiving a property that could either have null { "some_obj" : null } , an empty array [] { "some_obj" : [] }, or an object has is not null with some properties { "some_obj" : 'name' : 'nick' } as the value. In this context, an empty array should translate to null, so I created this converter.

public class MyConverter<T> : JsonConverter<T>
{
    public override T? Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        if(reader.TokenType == JsonTokenType.StartArray)
        {
            return default(T);
        }
        else if (reader.TokenType == JsonTokenType.StartObject)
        {
            return JsonSerializer.Deserialize<T>(ref reader, options);
        }
        else
        {
            return default(T);
        }
    }
  
    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
        => JsonSerializer.Serialize(writer, (T?)value, options);
}

When the data coming in is { "some_obj" : [] }, the first if statement is true and I get the following the JsonException when returning default(T): "The converter {converter} read too much or not enough." Any idea of what I'm doing wrong? When the data is { "some_obj" : null } or { "some_obj" : 'name' : 'nick' } , it's working fine.

I'm using the attribute on a type in one my classes

public class SomeClass
{
    [JsonConverter(typeof(MyConverter<SomeObj>))]
    [JsonPropertyName("some_obj")]
    public SomeObj? SomeObj{ get; set; }
}

public class SomeObj
{
    [JsonPropertyName("name")]
    public string? Name{ get; set; }
}

UPDATE Making sure I ended on JsonTokenType.EndArray was the solution. Thanks Simon for the ticket reference

public override T? Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options)
{
    if (reader.TokenType == JsonTokenType.StartObject)
    {
        return JsonSerializer.Deserialize<T>(ref reader, options);
    }

    if (reader.TokenType == JsonTokenType.StartArray)
    {
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndArray)
            {
                return default(T);
            }

        }
    }
    throw new JsonException();
}

CodePudding user response:

Inside JsonConverter<T>.Read(), if you want to ignore the contents of the current JSON token, you must advance the reader to end of the token, reading past any and all child tokens. The method Utf8JsonReader.Skip() does this:

public class MyConverter<T> : JsonConverter<T>
{
    public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        switch (reader.TokenType)
        {
            case JsonTokenType.StartObject:
                return JsonSerializer.Deserialize<T>(ref reader, options);
            default:
                {
                    reader.Skip();
                    return default(T);
                }               
        }
    }

This method Skips the children of the current JSON token which is necessary when the token to be ignored is an array (and does nothing in the event the current token is a primitive value such as null).

Demo fiddle here.

Notes:

  • Related