Search code examples
c#jsonserializationjson.netdeserialization

Deserializing Indexed Properties into an Array


Say I have some Json that will come in a packet like this:

{
    "LinkType1": "google",
    "LinkUrl1": "https://plus.google.com/test",
    "LinkShow1": 1,

    "LinkType2": "facebook",
    "LinkUrl2": "https://www.facebook.com/test",
    "LinkShow2": 0,

    "LinkType3": "linkedin",
    "LinkUrl3": "http://www.linkedin.com/test",
    "LinkShow3": 1,

    "count": 3,
    "errorCode": 0,
    "errorMessage": "Success"
}

Notice how everything comes back as the same property, but with an index on it?

I would love to be able to deserialize that data as though it was an array instead of single properties. What would be the best method for deserializing this into the classes below? I'm using the Newtonsoft Json library for serialization, so a solution using that would be preferred.

    public class LinksResult
    {
        public List<LinkData> Links { get; set; } 

        [JsonProperty("count")]
        public int Count { get; set; }

        [JsonProperty("errorCode")]
        public int ErrorCode { get; set; }

        [JsonProperty("errorMessage")]
        public string ErrorMessage { get; set; }
    }

    public class LinkData
    {
        public string LinkType { get; set; }
        public string LinkUrl { get; set; }
        public bool LinkShow { get; set; }
    }

Solution

  • You can use a custom JsonConverter to deserialize the JSON data into the structure that you want. Here is what the code for the converter might look like.

    class LinksResultConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(LinksResult));
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject obj = JObject.Load(reader);
            LinksResult result = new LinksResult();
            result.Count = (int)obj["count"];
            result.ErrorCode = (int)obj["errorCode"];
            result.ErrorMessage = (string)obj["errorMessage"];
            result.Links = new List<LinkData>();
    
            for (int i = 1; i <= result.Count; i++)
            {
                string index = i.ToString();
                LinkData link = new LinkData();
                link.LinkType = (string)obj["LinkType" + index];
                link.LinkUrl = (string)obj["LinkUrl" + index];
                link.LinkShow = (int)obj["LinkShow" + index] == 1;
                result.Links.Add(link);
            }
    
            return result;
        }
    
        public override bool CanWrite
        {
            get { return false; }
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    To use the converter, just add a [JsonConverter] attribute to your LinksResult class as shown below. (Note that you don't need the [JsonProperty] attributes with this approach, since the mapping between JSON property names and the actual class members is handled directly by the converter.)

    [JsonConverter(typeof(LinksResultConverter))]
    public class LinksResult
    {
        public List<LinkData> Links { get; set; }
        public int Count { get; set; }
        public int ErrorCode { get; set; }
        public string ErrorMessage { get; set; }
    }
    

    Then, you can deserialize like this:

    LinksResult result = JsonConvert.DeserializeObject<LinksResult>(json);
    

    Fiddle: https://dotnetfiddle.net/56b34H