2

I have JSON configuration files that I deserialize into c# classes. However, sometimes, I have a need to merge certain nodes of the config files. I can't simply serialize object 1 and object 2 and then merge because of the way null and default value handling work (if object 2 has a default value, it won't serialize and overwrite object 1's differing property value). I'd prefer to keep my default and null value handling untouched.

Is there some sort of metadata deserialization method that along with the object, I'd have access to the source json of that particular node? I know this is far reaching, but I'm not quite sure how else to handle my situation.

{
    "prop1": "value1",
    "prop2": "value2",
    "defaultProp4Options": {
        "subprop1": "subvalue1",
        "subprop2": "subvalue2",
        "subprop3": "subvalue3"
    },
    "prop4": [
        {
            "subprop4": "subvalue4"
        },
        {
            "subprop2": "diff2",
            "subprop3": "diff3",
        },
        {
            "subprop1": "diff1",
            "subprop5": "subvalue5",
        }
    ]
}

I'm thinking the only way to do this is to merge prior to deserialization, but I'm a little stuck.

var jfoo = JObject.Parse(myconfigjson);
var defaults = jfoo.Values("defaultProp4Options");
var bars = jfoo.Values("prop4");

// ***********
// How do I merge defaults with each bar (bar has precendence) and replace collection?
// ***********

// Use the new merged json to deserialize
var result = JsonConvert.DeserializeObject<Foo>(jfoo.ToString());

Can anybody get me on the right track?

UPDATE

This gets me there, but let me know if you know of a better way.

var foo = JObject.Parse(s);
var defaults = jfoo.GetValue("defaultProp4Options");
var bars = jfoo.GetValue("prop4");

var j = new JArray();
foreach (var bar in bars)
{
    var baseDefaults = defaults.DeepClone() as JObject;
    baseDefaults.Merge(bar);
    j.Add(baseDefaults);
}

jfoo.Remove("defaultProp4Options");
jfoo.Property("bars").Value = j;

Which results in:

{
    "prop1": "value1",
    "prop2": "value2",
    "prop4": [
        {
            "subprop1": "subvalue1",
            "subprop2": "subvalue2",
            "subprop3": "subvalue3",
            "subprop4": "subvalue4"
        },
        {
            "subprop1": "subvalue1",
            "subprop2": "diff2",
            "subprop3": "diff3",
        },
        {
            "subprop1": "diff1",
            "subprop2": "subvalue2",
            "subprop3": "subvalue3",
            "subprop5": "subvalue5"
        }
    ]
}
3
  • Let me tell you if I undestand you problem, generally speaking. if you have two strings s1 and s2 representing some data in JSON you want a method to "glue" these two s1 and s2 into a single JSON and then deserialize the result it into a .NET Object ? Commented Aug 6, 2015 at 16:25
  • @GeorgeLica I believe this is what he wants to do, with bars having precedence over foo. IMHO the best approach in this case would be to merge the json before deserializing. Commented Aug 6, 2015 at 16:53
  • Yes, I simply want to merge defaults with each bar. Json.Net has a JObject.Merge() method, but I don't know how to leverage it where commented in my last code example. Commented Aug 6, 2015 at 17:41

1 Answer 1

1

After 4th release of Json.Net version 6 it supports Merge operation based on JContainer.Merge method.

but unfortunately in your sample we can't use the method directly because defaultProp4Options is an object and prop4 is an array, but if the structure is not going to change heavily we can iterate in prop4 and merge the items instead, here is an example

        var jsonObj = JObject.Parse(json);

        var def = (JObject)jsonObj["defaultProp4Options"];
        var prop4 = jsonObj["prop4"];

        for(int i=0;i<prop4.Count();i++)
        {
            var item = prop4.ElementAt(i);
            var cloneDef =(JObject) def.DeepClone();
            cloneDef.Merge(item);
            item.Replace(cloneDef);
        }

        var mergedJson = jsonObj.ToString(); //only used to show new json
        Console.WriteLine(mergedJson);

        var foo = jsonObj.ToObject<Foo>(); //deserializing to foo

and here is the output result

{
  "prop1": "value1",
  "prop2": "value2",
  "defaultProp4Options": {
    "subprop1": "subvalue1",
    "subprop2": "subvalue2",
    "subprop3": "subvalue3"
  },
  "prop4": [
    {
      "subprop1": "subvalue1",
      "subprop2": "subvalue2",
      "subprop3": "subvalue3",
      "subprop4": "subvalue4"
    },
    {
      "subprop1": "subvalue1",
      "subprop2": "diff2",
      "subprop3": "diff3"
    },
    {
      "subprop1": "diff1",
      "subprop2": "subvalue2",
      "subprop3": "subvalue3",
      "subprop5": "subvalue5"
    }
  ]
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, this has the general idea, but I want to modify and retain each item in prop4. I don't want to modify defaultProp4Options. The items in prop4 are basically saying, "this is how I deviate from the defaults".
@JasonButera changed the answer accordingly
Thanks, I was updating my post at the same time!

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.