Home > Blockchain >  C# dynamic object, modify properties based on string paths
C# dynamic object, modify properties based on string paths

Time:09-24

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

CodePudding user response:

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.

CodePudding user response:

With Json.NET you can do that like this:

var json = File.ReadAllText("sample.json");
var semiParsedJson = JObject.Parse(json);
var instructions = (JArray)semiParsedJson["instructions"];
var @base = semiParsedJson["base"];

foreach (var instruction in instructions)
{
    var path = (string)instruction["patchedPath"];
    var newValue = (string)instruction["patchedValue"];
    var toBeReplaced = @base.SelectToken(path);
    toBeReplaced.Replace(newValue);              
}
  • JObject.Parse parses the json string
  • With the index operator [] we retrieve the two top level nodes. - One of them is an array (that's why there is an explicit JArray cast)
  • The other one is a JToken
  • We iterate through the array and retrieve the path and the newvalue
  • We use the SelectToken to get the desired node and then apply the replacement via the Replace method.

Please bear in mind that this solution is not bulletproof. You might need to change the indexer operator to TryGetValue to be able to check existence before you perform any operation on the JToken.

You also need to check that the patchedPath is valid at all.

  • Related