C#基于字符串路径修改动态JSON属性及GetProperty空引用报错解决
业务需求
收到的根级JSON payload包含两个属性:
instructionsbase
需要将instructions中的修改规则应用到base JSON对象上。举例来说,按照示例payload的规则,需要遍历到base属性中defaultWidget下的widgets节点,将其值完全替换为patchedValue对应的取值。
输入Payload
{ "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 } } }
现有问题
当前代码运行时触发空引用报错:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
System.Type.GetProperty(...) returned null.
报错原因是JsonConvert.DeserializeObject不带泛型时返回的是object类型,实际底层是Newtonsoft.Json的JObject实例,并非自定义类,无法通过反射GetProperty获取JSON属性。
当前编写的错误代码:
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);
解决方案
直接使用Newtonsoft.Json的JObject原生API处理JSON节点,完整可运行代码如下:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; public class Program { public static void Main() { 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\": \"\" } ] } }}"; // 反序列化为JObject类型 JObject parsedPayload = JsonConvert.DeserializeObject<JObject>(stringPayload); JArray instructions = parsedPayload["instructions"] as JArray; JObject baseNode = parsedPayload["base"] as JObject; // 遍历所有修改规则 foreach (JObject instruction in instructions) { string patchedPath = instruction["patchedPath"].ToString(); JToken patchedValue = instruction["patchedValue"]; string[] pathSegments = patchedPath.Split('.'); JToken currentNode = baseNode; // 逐层定位到目标节点的父节点 for (int i = 0; i < pathSegments.Length - 1; i++) { currentNode = currentNode[pathSegments[i]]; } // 赋值修改目标属性 (currentNode as JObject)[pathSegments[^1]] = patchedValue; } // 构造输出结果 var output = new { @base = baseNode }; string resultJson = JsonConvert.SerializeObject(output, Formatting.Indented); System.Console.WriteLine(resultJson); } }
该方案支持任意层级的patchedPath修改,直接调用JSON库原生API避免反射报错,运行后输出结果和预期完全一致。
内容的提问来源于stack exchange,提问作者SamuraiJack
相关产品推荐
相关产品推荐

