如何在Newtonsoft.Json.JObject中获取与设置嵌套数据并自动创建层级
如何用Newtonsoft.Json便捷创建多层级JSON结构并设置属性?
我用C#开发,和前端JavaScript交互时频繁使用Newtonsoft.Json的JObject类型。现在需要实现一个属性,将multipage.pages设置为字符串数组,但multipage元素可能不存在。想知道Newtonsoft.Json是否提供便捷方式,确保创建新元素时层级树中的所有父节点都自动存在?
现有尝试与问题
直接使用点号作为键的失败案例
尝试直接用带点号的字符串作为键,结果被当成了单个JSON键,而非层级结构:
JObject j = new JObject(); j["multipage.keepSave"] = true; Console.WriteLine(j.ToString());
输出结果:
{ "multipage.keepSave": true }
手动判断父节点的繁琐方案
手动检查每个父节点是否存在,虽然能实现需求,但每个属性都要重复编写判断逻辑,代码冗余:
void Main() { Form f = new Form(); Console.WriteLine(f.KeepSave); f.KeepSave = true; Console.WriteLine(f.KeepSave); f.KeepSave = false; Console.WriteLine(f.KeepSave); Console.WriteLine(f.Options.ToString()); } public class Form { public JObject Options = new JObject(); public bool KeepSave { get { JToken t = Options["multipage"]; if(t == null) return false; return (bool)t["keepsave"]; } set { JToken t = Options["multipage"]; if(t == null) Options["multipage"] = t = new JObject(); t["keepsave"] = value; } } }
自定义通用方法的尝试
我自己编写了一个通用方法来处理层级路径的设置,简化了属性的setter逻辑:
void Main() { Form f = new Form(); Console.WriteLine(f.KeepSave); f.KeepSave = true; Console.WriteLine(f.KeepSave); f.KeepSave = false; Console.WriteLine(f.KeepSave); Console.WriteLine(f.Options.ToString()); } static public void SetJObject(JToken o, string path, object value) { List<string> p = path.Split('.').ToList(); for(int i = 0; i < p.Count - 1; i++) { string name = p[i]; JToken j = o[name]; if(j == null) o[name] = j = new JObject(); o = j; } o[p[p.Count - 1]] = JToken.FromObject(value); } public class Form { public JObject Options = new JObject(); public bool KeepSave { get { JToken t = Options.SelectToken("multipage.keepsave"); return t == null ? false : (bool)t; } set { SetJObject(Options, "multipage.keepsave", value); } } }
改进建议
1. 优化通用方法的健壮性
针对原方法的不足,增加参数校验、无效路径处理和类型冲突检查:
static public void SetJObject(JToken root, string path, object value) { if (root == null) throw new ArgumentNullException(nameof(root)); if (string.IsNullOrEmpty(path)) throw new ArgumentException("路径不能为空", nameof(path)); // 过滤路径中的空字符串,处理开头/结尾带点号的情况 var segments = path.Split('.').Where(s => !string.IsNullOrWhiteSpace(s)).ToList(); if (segments.Count == 0) throw new ArgumentException("路径格式无效", nameof(path)); JToken current = root; for (int i = 0; i < segments.Count - 1; i++) { var segment = segments[i]; var next = current[segment]; if (next == null) { next = new JObject(); current[segment] = next; } else if (next.Type != JTokenType.Object) { // 父节点不是JObject,无法添加子节点,可根据需求抛出异常或做其他处理 throw new InvalidOperationException($"路径中的节点 '{segment}' 不是JObject,无法创建子节点"); } current = next; } current[segments.Last()] = JToken.FromObject(value); }
2. 简化取值逻辑
利用SelectToken的扩展能力,结合空合并运算符简化getter:
public bool KeepSave { get => Options.SelectToken("multipage.keepsave")?.ToObject<bool>() ?? false; set => SetJObject(Options, "multipage.keepsave", value); }
3. 封装为扩展方法
将通用方法改成JToken的扩展方法,使用更直观:
public static class JTokenExtensions { public static void SetPathValue(this JToken root, string path, object value) { // 上述优化后的SetJObject逻辑放在此处 } }
调用方式简化为:
Options.SetPathValue("multipage.keepsave", value);
4. 强类型映射(可选)
如果配置结构稳定,直接定义强类型类,完全避免手动操作JObject,编译时类型检查更严格:
public class FormOptions { public MultipageConfig Multipage { get; set; } = new MultipageConfig(); } public class MultipageConfig { public bool KeepSave { get; set; } public string[] Pages { get; set; } = Array.Empty<string>(); } public class Form { public FormOptions Options { get; set; } = new FormOptions(); public bool KeepSave { get => Options.Multipage.KeepSave; set => Options.Multipage.KeepSave = value; } public string[] Pages { get => Options.Multipage.Pages; set => Options.Multipage.Pages = value; } }
序列化时直接使用JsonConvert.SerializeObject(Options)即可得到所需的JSON结构,无需手动处理层级创建。
内容的提问来源于stack exchange,提问作者Nikki Locke
相关产品推荐
相关产品推荐

