Jason Butera
Jason Butera

Reputation: 2454

Merging source JSON before deserializing using Newtonsoft JSON.Net

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"
        }
    ]
}

Upvotes: 2

Views: 1411

Answers (1)

user3473830
user3473830

Reputation: 7295

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"
    }
  ]
}

Upvotes: 1

Related Questions