Search code examples
c#asp.net-core.net-corejson.net

C# dynamic object, modify properties based on string paths


The use case is pretty simple in concept. I receive a json payload that has two properties on the root level:

  1. instructions
  2. base

Instructions are set of instructions that I am supposed to apply on the base json object.

For eg - according to the below payload,

  • I am supposed to traverse to the widgets within defaultWidgets of the base property.
  • Then replace it completely with whatever is the value of patchedValue.

Input Payload:

{
    "instructions": [
        {
            "patchedPath": "defaultWidget.widgets",
            "patchedValue": false,
        }
    ],
    "base": {
        "defaultWidget": {
            "hash": "ktocle2l0u527",
            "layout": "6|6",
            "managerId": "defaultWidget",
            "widgets": [
                {
                    "managerId": "defaultWidget",
                    "widgetId": "invCreateWid7",
                    "type": "standard",
                    "manifestPath": "[email protected]",
                    "defaultInputManifestPath": "[email protected]",
                    "title": "scannedInvoice",
                    "children": [
                        {
                            "name": "tom"
                        }
                    ],
                    "hash": "ktocle2lrgps9",
                    "directives": ""
                }
            ]
        }
    }
}

The result should be :

{    
    "base": {
        "defaultWidget": {
            "hash": "ktocle2l0u527",
            "layout": "6|6",
            "managerId": "defaultWidget",
            "widgets": false
        }
    }
}

Code:

var stringPayload = "{    \"instructions\": [        {            \"patchedPath\": \"defaultWidget.widgets\",            \"patchedValue\": false,        }    ],    \"base\": {        \"defaultWidget\": {            \"hash\": \"ktocle2l0u527\",            \"layout\": \"6|6\",            \"managerId\": \"defaultWidget\",            \"widgets\": [                {                    \"managerId\": \"defaultWidget\",                    \"widgetId\": \"invCreateWid7\",                    \"type\": \"standard\",                    \"manifestPath\": \"[email protected]\",                    \"defaultInputManifestPath\": \"[email protected]\",                    \"title\": \"scannedInvoice\",                    \"children\": [                        {                            \"name\": \"tom\"                        }                    ],                    \"hash\": \"ktocle2lrgps9\",                    \"directives\": \"\"                }            ]        }    }}";
var parsedPayload = JsonConvert.DeserializeObject(stringPayload);
var baseJ = parsedPayload.GetType().GetProperty("instructions").GetValue(parsedPayload, null);
string jsonString = JsonConvert.SerializeObject(parsedPayload);

I am stuck on the very initial steps , I am getting:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

System.Type.GetProperty(...) returned null.

This is what QuickWatch says:

enter image description here


Solution

  • What's returned by DeserializeObject in this case is JObject so to start with you can cast to it:

    var parsedPayload = (JObject) JsonConvert.DeserializeObject(stringPayload);
    

    Then grab instructions and target to change:

    var instructions = (JArray) parsedPayload["instructions"]; // cast to JArray
    var result = parsedPayload["base"];
    

    Then we can go over instructions and apply them:

    foreach (var instruction in instructions) {
        // grab target path and value
        var targetPath = (string) ((JValue)instruction["patchedPath"]).Value;
        var targetValue = (JValue)instruction["patchedValue"];
        // temp variable to traverse the path
        var target = result;
        foreach (var part in targetPath.Split('.')) {
            target = target[part];
        }
        // replace the value
        target.Replace(targetValue);
    }
    

    Now result contains what was in base with instructions applied.