2
votes

I have problems parsing Last.FM API JSON with JSON.NET. The problem is with string values that are meant to be null, like similar an tags in: http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=ASASAS&api_key=53aed44f6fa2ee83d40324232594e1d9&format=json

Otherwise, the object is well-formed: http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=Kamelot&api_key=53aed44f6fa2ee83d40324232594e1d9&format=json

[JsonConverter(typeof(StringNullConverter<Tags>))]
public class Tags
{
            public List<Tag> tag { get; set; }
}

  internal class StringVacioConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(string)) || (objectType == typeof(List<string>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
        {
            return null;
        }
        else { return serializer.Deserialize<T>(reader); }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }
}

But this, is not working, it enters a infinite loop when tries to return serializer.Deserialize...

2

2 Answers

2
votes

This code works for both of your json strings (only JsonConvert.DeserializeObject needs some help with a custom Converter (see the MyConverter below)).

var lastfm = LastFM.Deserialize(json);

public class LastFM
{
    public class Image
    {
        [JsonProperty("#text")]
        public string text { get; set; }
        public string size { get; set; }
    }

    public class Stats
    {
        public string listeners { get; set; }
        public string playcount { get; set; }
    }

    public class Similar
    {
        public List<Artist> artist { get; set; }
    }

    public class Tag
    {
        public string name { get; set; }
        public string url { get; set; }
    }

    public class Tags
    {
        public List<Tag> tag { get; set; }
    }

    public class Link
    {
        [JsonProperty("#text")]
        public string text { get; set; }
        public string rel { get; set; }
        public string href { get; set; }
    }

    public class Links
    {
        public Link link { get; set; }
    }

    public class Bio
    {
        public Links links { get; set; }
        public string published { get; set; }
        public string summary { get; set; }
        public string content { get; set; }
    }

    public class Artist
    {
        public string name { get; set; }
        public string mbid { get; set; }
        public string url { get; set; }
        public List<Image> image { get; set; }
        public string streamable { get; set; }
        public string ontour { get; set; }
        public Stats stats { get; set; }
        public Similar similar { get; set; }
        public Tags tags { get; set; }
        public Bio bio { get; set; }
    }

    public class RootObject
    {
        public Artist artist { get; set; }
    }

    public static RootObject Deserialize(string s)
    {
        return JsonConvert.DeserializeObject<RootObject>(s, new MyConverter());
    }

    class MyConverter : JsonConverter
    {

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Similar) ||
                    objectType == typeof(Tags);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.Value is string && String.IsNullOrWhiteSpace((string)reader.Value)) return null;
            return reader.Value;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
}
1
votes

Try to adjust your Converter like this:

// I. here we inherit from more sophisticated class, created for us
internal class StringNullConverter<T> : CustomCreationConverter<T>
    where T : new()
{
    // II. thanks the new() constraint we can override this method easily
    public override T Create(Type objectType)
    {
        return new T();
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(string)) || (objectType == typeof(List<string>));
    }

    // III. here we pass the rest to smart base
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.String)
        {
            return null;
        }
        // this is the correct implementation
        return base.ReadJson(reader, objectType, existingValue, serializer);

    }
    // unchanged
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }
}

We do profit from full power of the Newtonsoft.Json, which brings lot of pre-implemented stuff for us