4

I have JSON:

{
"One": [
{
  "ID": 1,
  "name": "s"
},
{
  "categoryID": 2,
  "name": "c"
}
],
"Two": [
{
  "ID": 3,
  "name": "l"
}
],
"Three": [
{
  "ID": 8,
  "name": "s&P"
},
{
  "ID": 52,
  "name": "BB"
}
]
}

I want to:

  1. Take this JSON to any object(like JObject)

  2. Filter the JSON on different conditions(like name start with s, etc

  3. Return this JSON to client

Things I have tried: 1. Creating models:

class Model
{
public int Id;
public string name;
}
class MainModel
{
public string mainName;
public List<Model> categories;
}

And use these model to:

List<MainModel> m = json_serializer.DeserializeObject(jsonString);
  1. I tried to Dictionary also but getting unable to cast exception.

any help would be appreciated.

4
  • Did you try Dictionary<string, List<Model>>? Commented Aug 9, 2017 at 2:40
  • 1
    var jObj = JObject.Parse(jsonString); Now you have more than a dictionary. newtonsoft.com/json/help/html/… Commented Aug 9, 2017 at 2:49
  • I have tried Dictionary: Dictionary<string, List<Model>> dct = (Dictionary <string, List< Model >>) json_serializer.DeserializeObject(json); I am getting Invalid cast exception Commented Aug 9, 2017 at 2:55
  • Obviously you will get invalid cast exception.Your model is not consistent with the JSON Commented Aug 9, 2017 at 6:01

2 Answers 2

6

The following will allow you to deserialize your JSON to a Dictionary and filter on it. This uses Newtonsoft.Json Nuget package, but that's fairly common. Code posted below. Working example found here .Net Fiddle.

using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;


public class Program
{
    public static void Main()
    {
        var json = "{\"One\": [{  \"ID\": 1,  \"name\": \"s\"},{  \"categoryID\": 2,  \"name\": \"c\"}],\"Two\": [{  \"ID\": 3,  \"name\": \"l\"}],\"Three\": [{  \"ID\": 8,  \"name\": \"s&P\"},{  \"ID\": 52,  \"name\": \"BB\"}]}";      
        var deserialized = JsonConvert.DeserializeObject<Dictionary<string, List<Model>>>(json);

        Console.WriteLine(deserialized["One"][0].name);

        Console.WriteLine("Filter to starts with s");
        var filtered = deserialized.SelectMany(item => item.Value).Where(innerItem => innerItem.name.StartsWith("s"));
        foreach(var item in filtered){
            Console.WriteLine(item.name);   
        }

    }

    public class Model{
        public int ID {get;set;}
        public string name {get;set;}
        public int categoryID {get;set;}
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

I have written this code assuming that:

  • One, Two and Three are a MainCategory -which is seralized as JProperty from a KeyValuePair<string name, List<SubCategory> values>. Long type name, eh? Add the following if you want to use the result dictionary often:

    using MainCategories = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<JsonDeSerializtionTest.SubCategory>>;
    
  • categoryID and ID are properties that descibe an Id and can not co-exist on the same SubCategory but do change the type of SubCategory.

1. Create the SubCategory class:

public class SubCategory
{
    [JsonIgnore]
    public DataTypes DataType { get; set; }

    [JsonIgnore]
    public string Parent { get; set; }

    [JsonProperty("ID")]
    public int Id { get; set; }

    [JsonProperty("categoryID")]
    public int CategoryId => Id;

    [JsonProperty("name")]
    public string Name { get; set; }

    public bool ShouldSerializeId()
    {
        return DataType == DataTypes.Id;
    }

    public bool ShouldSerializeCategoryId()
    {
        return DataType == DataTypes.CategoryId;
    }

}

2. Create the Deserialize(string json) function:

private static MainCategories Deserialize(string json)
{
    Dictionary<string, List < SubCategory >> jsonBody = new Dictionary<string, List<SubCategory>>();

    JObject jObject = JObject.Parse(json);
    // the outer object {one...two..}

    foreach (KeyValuePair<string, JToken> jMainCategory in jObject)
    {
        // jMainCategory => "one": [{...}, {...}]
        string key = jMainCategory.Key;
        List<SubCategory> values = new List<SubCategory>();
        foreach (JObject jSubCategory in jMainCategory.Value)
        {
            //jsubCategory => {"name" : ..., "ID": ... }
            SubCategory subCategory = new SubCategory();

            JToken idProperty;
            if (jSubCategory.TryGetValue("ID", out idProperty))
            {
                subCategory.DataType = DataTypes.Id;
                subCategory.Id = idProperty.Value<int>();
            }
            else
            {
                subCategory.DataType = DataTypes.CategoryId;
                subCategory.Id = jSubCategory["categoryID"].Value<int>();
            }

            subCategory.Name = jSubCategory["name"].Value<string>();
            subCategory.Parent = key;
            // subCategory.AnotherProperty = jSubCategory["anotherproperty"].Value<type>();

            values.Add(subCategory);
        }
        jsonBody.Add(key, values);
    }
    return jsonBody;
}

3. Use the function to get a Dictionary<string, List<SubCategory>> which you can use for sorting and filtering. Example:

public static MainCategories WhereNameStartsWith(this MainCategories jsonBody, string str)
{
    MainCategories result = new MainCategories();
    //if you want to keep the result json structure `as is` return a MainCategories object

    foreach (var subCategory in jsonBody.SelectMany(mainCategory => mainCategory.Value).Where(subCategory => subCategory.Name.StartsWith(str)))
    {
        if(result.ContainsKey(subCategory.Parent))
            result[subCategory.Parent].Add(subCategory);
        else
            result.Add(subCategory.Parent, new List<SubCategory> {subCategory});
    }
    // if you just want the subcategories matching the condition create a WhereListNameStartsWith method
    // where `result` is a list of subcategories matching the condition
    return  result;
}    

Upside:

  • No playing around with NewtonSoft JsonConverter or ContractResolvers.

  • The ability to use LINQ to easily sort Name AND CategoryId/Id which have the same value but make a different SubCategory.DataType:

    foreach (var subCategory in jsonBody.SelectMany(mainCategory => mainCategory.Value).Where(subCategory => subCategory.DataType == DataTypes.CategoryId))
    
  • You can easily serialize the JSON back to string representation. Example:

    string jsonIn ="{\"One\":[{\"ID\":1,\"name\":\"s\"}," +
                                 "{\"categoryID\":2,\"name\":\"c\"}]," +
                    "\"Two\":[{\"ID\":3,\"name\":\"l\"}]," +
                    "\"Three\":[{\"ID\":8,\"name\":\"s&P\"}," +
                                  "{\"ID\":52,\"name\":\"BB\"}]}";
    MainCategories desrializedJson = Deserialize(jsonIn);
    MainCategories filtered = desrializedJson.WhereNameStartsWith("s");
    string jsonOut = JsonConvert.SerializeObject(desrializedJson, Formatting.None);
    Debug.Assert(jsonOut == jsonIn); //true
    
  • Removes the need for null checking when accessing Id/CategoryId.

  • My favorite: using C# syntax for property names.

Downside:

  • I don't know. This is a paste and forget soltuion :P

    Probably a harder time when changing your JSON structure (although I might argue it would make things much easier for you)

Notes:

  • Parent is optional and is used to make things easier when using LINQ
  • DataTypes is an enum which is used to determine wether ID or categoryID was found if you want such information and also to serialize the proper property (identical two way convertion)

    public enum DataTypes
    {
        Id,
        CategoryId
    };
    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.