C#中如何通过JSON配置动态修改动态对象的指定属性
解决方案:基于路径字符串修改C#动态JSON对象
核心思路
我们需要解析配置中的路径字符串(支持属性访问.key和数组索引[0]),遍历目标JSON对象找到对应节点后完成赋值。同时通过简单的字符串处理取消jsonObj前缀限制。
推荐使用Newtonsoft.Json的JObject类型操作(默认反序列化dynamic时实际就是JObject),相比ExpandoObject更稳定且原生支持JSON结构操作。
实现代码(JObject方案)
1. 工具方法封装
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; public static class JsonPathSetter { /// <summary> /// 根据路径字符串设置JObject的值 /// </summary> /// <param name="root">目标JSON根对象</param> /// <param name="path">配置中的路径字符串</param> /// <param name="value">要设置的值</param> /// <param name="createIfMissing">路径不存在时是否自动创建节点</param> public static void SetValueByPath(JObject root, string path, object value, bool createIfMissing = true) { // 移除可选的jsonObj前缀 if (path.StartsWith("jsonObj.")) { path = path.Substring("jsonObj.".Length); } else if (path.Equals("jsonObj", StringComparison.OrdinalIgnoreCase)) { // 直接替换整个根对象 root.ReplaceAll(JToken.FromObject(value)); return; } var pathSegments = path.Split('.'); JToken current = root; // 遍历到目标节点的父节点 for (int i = 0; i < pathSegments.Length - 1; i++) { current = GetNextToken(current, pathSegments[i], createIfMissing); if (current == null) { throw new ArgumentException($"路径不存在:{path}"); } } // 设置最后一个节点的值 SetLastTokenValue(current, pathSegments[^1], value); } private static JToken GetNextToken(JToken current, string segment, bool createIfMissing) { // 处理数组索引格式(如key[0]或[0]) if (segment.Contains("[")) { var parts = segment.Split(new[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries); return parts.Length switch { // 直接数组索引(如[0]) 1 => HandleArrayIndex(current, int.Parse(parts[0]), createIfMissing), // 属性+数组索引(如key[0]) 2 => HandlePropertyArray(current, parts[0], int.Parse(parts[1]), createIfMissing), _ => throw new ArgumentException($"无效的路径段:{segment}") }; } // 处理普通属性 else { var prop = current[segment]; if (prop == null && createIfMissing) { prop = new JObject(); ((JObject)current)[segment] = prop; } return prop; } } private static JToken HandleArrayIndex(JToken current, int index, bool createIfMissing) { if (current is not JArray array) { throw new InvalidOperationException($"当前节点不是数组,无法使用索引:[{index}]"); } if (index >= array.Count) { if (!createIfMissing) return null; // 自动填充数组到指定索引 while (array.Count <= index) { array.Add(new JObject()); } } return array[index]; } private static JToken HandlePropertyArray(JToken current, string propName, int index, bool createIfMissing) { if (current is not JObject obj) { throw new InvalidOperationException($"当前节点不是对象,无法访问属性:{propName}"); } var prop = obj[propName]; if (prop == null) { if (!createIfMissing) return null; prop = new JArray(); obj[propName] = prop; } if (prop is not JArray array) { throw new InvalidOperationException($"属性{propName}不是数组,无法使用索引:[{index}]"); } if (index >= array.Count && createIfMissing) { while (array.Count <= index) { array.Add(new JObject()); } } return array[index]; } private static void SetLastTokenValue(JToken parent, string lastSegment, object value) { if (lastSegment.Contains("[")) { var parts = lastSegment.Split(new[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 1) { // 设置数组元素值 if (parent is JArray array) { int index = int.Parse(parts[0]); array[index] = JToken.FromObject(value); } else { throw new InvalidOperationException($"父节点不是数组,无法设置索引:{lastSegment}"); } } else if (parts.Length == 2) { // 设置数组内对象的属性值 if (parent is JObject obj) { var prop = obj[parts[0]] as JArray; prop[int.Parse(parts[1])] = JToken.FromObject(value); } else { throw new InvalidOperationException($"父节点不是对象,无法设置属性:{parts[0]}"); } } } else { // 设置普通属性值 if (parent is JObject obj) { obj[lastSegment] = JToken.FromObject(value); } else { throw new InvalidOperationException($"父节点不是对象,无法设置属性:{lastSegment}"); } } } }
2. 使用示例
// 待修改的目标JSON string targetJson = @" { ""someOuterKey"": { ""someInnerKey"": { ""someEvenMoreInnerKey"": ""someCurrentValue"" } }, ""arrayKey"": [{""name"": ""oldName""}, {""name"": ""oldName2""}] }"; // 配置文件JSON(支持带/不带jsonObj前缀) string configJson = @" { ""someOuterKey.someInnerKey.someEvenMoreInnerKey"": ""newValue"", ""jsonObj.arrayKey[1].name"": ""newName2"", ""jsonObj.newOuterKey.newInnerKey"": ""newAddedValue"" }"; // 反序列化目标对象和配置 JObject targetObj = JsonConvert.DeserializeObject<JObject>(targetJson); Dictionary<string, object> config = JsonConvert.DeserializeObject<Dictionary<string, object>>(configJson); // 遍历配置完成赋值 foreach (var kvp in config) { JsonPathSetter.SetValueByPath(targetObj, kvp.Key, kvp.Value); } // 输出修改后的JSON string updatedJson = targetObj.ToString(Formatting.Indented); Console.WriteLine(updatedJson);
关键特性说明
- 前缀兼容:自动识别并移除
jsonObj.前缀,用户可以自由选择是否添加 - 路径解析:支持普通属性(
.key)、数组索引([0])、属性+数组索引(key[0])三种格式 - 自动创建节点:开启
createIfMissing参数后,路径不存在时会自动创建对应的JObject/JArray - 类型安全:针对不同节点类型(对象/数组)做了校验,避免运行时错误
内容的提问来源于stack exchange,提问作者justanotherguy
相关产品推荐
相关产品推荐

