Search code examples
c#.net-5system.text.jsonlast.fm

System.Text.Json - Deserialize object that can be either an empty string or a class


I'm using System.Text.Json to deserialize some json. (More specifically, this call from the Last.fm API in json format)

The json that I'm trying to deserialize has a quite unconventional way of handling null values for some objects, for example when its null I get this:

    "tags": "",

And like this when it has values:

    "tags": {
        "tag": [
            {
                "name": "classic rock",
                "url": "https://www.last.fm/tag/classic+rock"
            },
            {
                "name": "rock",
                "url": "https://www.last.fm/tag/rock"
            }
        ]
    }

My C# class looks like this:

public class Artist
{
    public Tags Tags { get; set; }
}

public class Tags
{
    public Tag[] Tag { get; set; }
}

public class Tag
{
    public string Name { get; set; }
    public string Url { get; set; }
}

How would I check if an object is an empty string before actually trying to deserialize it?

When I try to deserialize it:

var deserializedObject = JsonSerializer.Deserialize<T>(requestBody);

It throws a System.Text.Json.JsonException: The JSON value could not be converted to FMBot.LastFM.Domain.Models.Tags. Path: $.artist.tags error.

This call used to work when the value was actually null, but now that it's an empty string it is broken and I can't figure out a solution.


Solution

  • I would use a custom converter for this task. For example:

    public class TagsConverter : JsonConverter<Tags>
    {
        public override Tags Read(ref Utf8JsonReader reader, Type typeToConvert, 
            JsonSerializerOptions options)
        {
            // If the token is a string, return null
            if(reader.TokenType == JsonTokenType.String)
            {
                return null;
            }
    
            // Skip over the object and property name
            reader.Read(); // Object
            reader.Read(); // Property name
    
            // Read the tags array
            var tags = JsonSerializer.Deserialize<Tag[]>(ref reader, options);
    
            reader.Read(); // Object
    
            return new Tags { Tag = tags};
        }
    
        public override void Write(Utf8JsonWriter writer, Tags value, 
            JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }
    

    And use it like this:

    var options = new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true,
        Converters = { new TagsConverter() }
    };
    
    var deserializedObject = JsonSerializer.Deserialize<Artist>(requestBody, options );