views:

88

answers:

2

Is there a library (C# preferred) to resolve what I would call multi-level cascading JSON?

Here is an example of what I mean: (Pseudocode/C#)

var json1 = @"{
    ""firstName"": ""John"",
    ""lastName"": ""Smith""
    }";

var json2 = @"{
    ""firstName"": ""Albert""
    }";

var json3 = @"{
    ""phone"": ""12345""
    }";

var cascadingJSON = JSON.Cascade(json1, json2, json3);

Result (Same behavior as CSS)

{
    "firstName"": "Albert", /*Overridden*/
    "lastName"": "Smith", /*Inherited*/
    "phone"": "12345"   }"; /*Added*/
}

Edit 1 - More complex example

const string json1 =
                @"{
                     ""firstName"": ""John"",
                     ""lastName"": ""Smith"",
                     ""age"": 25,
                     ""address"": 
                     {
                         ""streetAddress"": ""21 2nd Street"",
                         ""city"": ""New York"",
                         ""state"": ""NY"",
                         ""postalCode"": ""10021""
                     },
                     ""phoneNumber"": 
                     [
                         {
                           ""type"": ""home"",
                           ""number"": ""212 555-1234""
                         },
                         {
                           ""type"": ""fax"",
                           ""number"": ""646 555-4567""
                         }
                     ]
                 }";

            const string json2 =
                @"{
                     ""firstName"": ""John2"",
                     ""lastName"": ""robert"",
                     ""age"": 25,
                     ""address"": 
                     {
                         ""state"": ""FL"",
                     },
                     ""phoneNumber"": 
                     [
                         {
                           ""type"": ""fax"",
                           ""number"": ""222 222-2222""
                         },
                         {
                           ""type"": ""iphone"",
                           ""number"": ""111 111-1111""
                         }
                     ]
                 }";

            const string json3 =
                @"{
                     ""firstName"": ""John3"",
                     ""father"": ""guy""
                 }";

            const string expectedResult =
                @"{
                     ""firstName"": ""John3"",
                     ""lastName"": ""robert"",
                     ""age"": 25,
                     ""father"": ""guy"",
                     ""address"": 
                     {
                         ""streetAddress"": ""21 2nd Street"",
                         ""city"": ""New York"",
                         ""state"": ""FL"",
                         ""postalCode"": ""10021""
                     },
                     ""phoneNumber"": 
                     [
                         {
                           ""type"": ""home"",
                           ""number"": ""212 555-1234""
                         },
                         {
                           ""type"": ""fax"",
                           ""number"": ""222 222-2222""
                         },
                         {
                           ""type"": ""iphone"",
                           ""number"": ""111 111-1111""
                         }
                     ]
                 }";

Edit 2

After thinking a bit more about the requirements I see that the more complex example could never work as is. The Cascading function would not be able to know if, by example, a certain phone number has been modified or if it is new one. To make it work, each sub-entity should have a unique identifier.

A: 

Not that I know of. For the simple case, you can use any JSON library, then merge the dictionaries with a solution like this one. E.g. using Newtonsoft/Json.NET:

Dictionary<String, String> dict1, dict2, dict3, merged;
dict1 = JsonConvert.DeserializeObject<Dictionary<string,string>>(json1);
dict2 = JsonConvert.DeserializeObject<Dictionary<string,string>>(json2);
dict3 = JsonConvert.DeserializeObject<Dictionary<string,string>>(json3);
merged = Merge(new[]{dict1, dict2, dict3});

Obviously, in production code you'd factor out the redundant lines.

Matthew Flaschen
Thank you for your answer. As you say, it may work for simple case but I need it to work for all scenarios.
W3Max
+4  A: 

This is super easy using the excellent JSON.NET library. This method combine objects with properties that are are strings, numbers, or objects.

public static string Cascade(params string[] jsonArray)
{
    JObject result = new JObject();
    foreach (string json in jsonArray)
    {
        JObject parsed = JObject.Parse(json);
        foreach (var property in parsed)
            result[property.Key] = property.Value;
    }
    return result.ToString();
}

Result, given your example:

{
  "firstName": "Albert",
  "lastName": "Smith",
  "phone": "12345"
}

Edit in response to your updated question:

By adjusting this solution work recursively, you can merge child objects. The following example will match your expected results (except for the array). You will be able to easily extend this solution to merge arrays (JArray) in a manner similar to how it merges objects (JObject).

public static string Cascade(params string[] jsonArray)
{
    JObject result = new JObject();
    foreach (string json in jsonArray)
    {
        JObject parsed = JObject.Parse(json);
        Merge(result, parsed);
    }
    return result.ToString();
}

private static void Merge(JObject receiver, JObject donor)
{
    foreach (var property in donor)
    {
        JObject receiverValue = receiver[property.Key] as JObject;
        JObject donorValue = property.Value as JObject;
        if (receiverValue != null && donorValue != null)
            Merge(receiverValue, donorValue);
        else
            receiver[property.Key] = property.Value;
    }
}
Greg
+1 for JSON.NET, an excellent library.
Danny Chen
Thank you for your answer. The problem with this solution is that it only works with simple JSON objects. My example was lite JSON just for the clarity of the question. I edited my example for a more complex/detailed example.
W3Max