Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserializing JSON using custom deserializer with JSON.Net

I have JSON that looks like this:

{
  "MobileSiteContents": {
    "au/en": [
      "http://www.url1.com",
      "http://www.url2.com",

    ],
    "cn/zh": [
      "http://www.url2643.com",

    ]
  }
}

I'm trying to deserialize it into an IEnumerable of classes that look like this:

public class MobileSiteContentsContentSectionItem : ContentSectionItem
{
    public string[] Urls { get; set; }
}

public abstract class ContentSectionItem
{
    public string Culture { get; set; }
}

Is that possible?
I realise I will probably need to use a Custom JsonConverter for this, but can't find any examples.

I started writing a method to convert using JObject.Parse but not sure if this is the correct / most efficient route to go down:

public IEnumerable<MobileSiteContentsContentSectionItem> Parse(string json)
{
    var jobject = JObject.Parse(json);

    var result = new List<MobileSiteContentsContentSectionItem>();

    foreach (var item in jobject.Children())
    {
        var culture = item.Path;
        string[] urls = new[] { "" }; //= this is the part I'm having troble with here...

        result.Add(new MobileSiteContentsContentSectionItem { Culture = culture, Urls = urls });
    }

    return result;
}
like image 700
Alex Avatar asked Oct 17 '13 16:10

Alex


People also ask

How do I use JSON deserializer?

A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.

What is JSON deserializer?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).


2 Answers

You're on the right track. Here are the corrections you need to make:

  1. You're iterating over children of the top-level object expecting to get data that is actually in an object one level further down. You need to navigate to the value of the MobileSiteContents property and iterate over the children of that.
  2. When you take the Children() of the JObject, use the overload that lets you cast them to JProperty objects; that will make it much easier to extract the data you want.
  3. Get the culture from the Name of the JProperty item
  4. To get the urls, get the Value of the JProperty item and use ToObject<string[]>() to convert it to a string array.

Here is the corrected code:

public IEnumerable<MobileSiteContentsContentSectionItem> Parse(string json)
{
    var jObject = JObject.Parse(json);

    var result = new List<MobileSiteContentsContentSectionItem>();

    foreach (var item in jObject["MobileSiteContents"].Children<JProperty>())
    {
        var culture = item.Name;
        string[] urls = item.Value.ToObject<string[]>();

        result.Add(new MobileSiteContentsContentSectionItem { Culture = culture, Urls = urls });
    }

    return result;
}

If you like terse code, you can reduce this to a "one-liner":

public IEnumerable<MobileSiteContentsContentSectionItem> Parse(string json)
{
    return JObject.Parse(json)["MobileSiteContents"]
        .Children<JProperty>()
        .Select(prop => new MobileSiteContentsContentSectionItem
        {
            Culture = prop.Name,
            Urls = prop.Value.ToObject<string[]>()
        })
        .ToList();
}

Demo:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        {
            ""MobileSiteContents"": {
                ""au/en"": [
                    ""http://www.url1.com"",
                    ""http://www.url2.com"",
                ],
                ""cn/zh"": [
                    ""http://www.url2643.com"",
                ]
            }
        }";

        foreach (MobileSiteContentsContentSectionItem item in Parse(json))
        {
            Console.WriteLine(item.Culture);
            foreach (string url in item.Urls)
            {
                Console.WriteLine("  " + url);
            }
        }
    }

    public static IEnumerable<MobileSiteContentsContentSectionItem> Parse(string json)
    {
        return JObject.Parse(json)["MobileSiteContents"]
            .Children<JProperty>()
            .Select(prop => new MobileSiteContentsContentSectionItem()
            {
                Culture = prop.Name,
                Urls = prop.Value.ToObject<string[]>()
            })
            .ToList();
    }

    public class MobileSiteContentsContentSectionItem : ContentSectionItem
    {
        public string[] Urls { get; set; }
    }

    public abstract class ContentSectionItem
    {
        public string Culture { get; set; }
    }
}

Output:

au/en
  http://www.url1.com
  http://www.url2.com
cn/zh
  http://www.url2643.com
like image 69
Brian Rogers Avatar answered Oct 18 '22 05:10

Brian Rogers


I tried this using Json.Net and works fine.

public IEnumerable<MobileSiteContentsContentSectionItem> Parse(string json)
{
     dynamic jobject = Newtonsoft.Json.JsonConvert.DeserializeObject(json);

     var result = new List<MobileSiteContentsContentSectionItem>();

     var urls = new List<string>();
     foreach (var item in jobject.MobileSiteContents)
     {
         var culture = item.Name;
         foreach(var url in item.Value)
            urls.Add(url.Value);

         result.Add(new MobileSiteContentsContentSectionItem { Culture = culture, Urls = urls.ToArray() });
     }

     return result;
}
like image 43
Esteban Elverdin Avatar answered Oct 18 '22 04:10

Esteban Elverdin