C#动态对象,根据字符串路径修改属性

C# dynamic object, modify properties based on string paths

用例在概念上非常简单。我收到一个 json 负载,它在根级别上有两个属性:

  1. instructions
  2. base

说明是我应该应用到基础 json 对象上的一组说明。

例如-根据以下有效载荷,

输入负载:

{
    "instructions": [
        {
            "patchedPath": "defaultWidget.widgets",
            "patchedValue": false,
        }
    ],
    "base": {
        "defaultWidget": {
            "hash": "ktocle2l0u527",
            "layout": "6|6",
            "managerId": "defaultWidget",
            "widgets": [
                {
                    "managerId": "defaultWidget",
                    "widgetId": "invCreateWid7",
                    "type": "standard",
                    "manifestPath": "nexxe.standard-section@0.0.0-next.11",
                    "defaultInputManifestPath": "nexxe.input@0.0.1-alpha.49",
                    "title": "scannedInvoice",
                    "children": [
                        {
                            "name": "tom"
                        }
                    ],
                    "hash": "ktocle2lrgps9",
                    "directives": ""
                }
            ]
        }
    }
}

结果应该是:

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

代码:

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\": \"nexxe.standard-section@0.0.0-next.11\",                    \"defaultInputManifestPath\": \"nexxe.input@0.0.1-alpha.49\",                    \"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);

我卡在最开始的步骤上了,我得到:

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

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

QuickWatch 是这么说的:

在这种情况下,DeserializeObject 返回的是 JObject,因此您可以首先转换为它:

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

然后抓指令和目标改变:

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

然后我们可以查看说明并应用它们:

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);
}

现在结果包含应用指令后的基础内容。

使用 Json.NET 你可以这样做:

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 解析 json 字符串
  • 使用索引运算符 [] 我们检索两个顶级节点。 - 其中之一是一个数组(这就是为什么有一个明确的 JArray 转换)
  • 另一个是JToken
  • 我们遍历数组并检索 pathnewvalue
  • 我们使用 SelectToken 获取所需的节点,然后通过 Replace 方法应用替换。

请记住,此解决方案并非万无一失。您可能需要将索引器运算符更改为 TryGetValue,以便能够在对 JToken.

执行任何操作之前检查是否存在

您还需要检查 patchedPath 是否有效。