如何用Newtonsoft Json的MergeArrayHandling.Merge实现指定数组合并
自定义JSON数组合并(基于属性匹配)
首先可以明确:默认的MergeArrayHandling.Merge无法直接实现你想要的效果,因为它是按数组的位置索引来合并元素,而不是根据对象的特定属性(比如name或first)来匹配。你之前尝试的代码会按索引合并外层数组的元素,对于innerArray也是按索引合并,这样会丢失原数组中first=2的元素,或者覆盖成新的first=3的元素,不符合你的需求。
要实现你期望的合并逻辑(外层按name匹配,内层按first匹配更新、新增元素),需要手动处理合并逻辑,而不是依赖默认的Merge方法。下面是具体的实现方案:
实现步骤与代码示例
1. 核心思路
- 把外层数组转换为以
name为键的字典,快速匹配相同名称的对象; - 对每个匹配到的对象,再把
innerArray转换为以first为键的字典,实现按first匹配更新second值,新增不存在的元素; - 最后将字典转换回数组,并保持有序。
2. 完整代码
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; class Program { static void Main() { // 解析原始JSON数组 JArray o1 = JArray.Parse(@"[ {""name"": ""a"", ""innerArray"": [{""first"": 1, ""second"": 1}, {""first"": 2, ""second"": 2}]}, {""name"": ""b"", ""innerArray"": [{""first"": 1, ""second"": 1}, {""first"": 2, ""second"": 2}]} ]"); JArray o2 = JArray.Parse(@"[ {""name"": ""a"", ""innerArray"": [{""first"": 1, ""second"": 11}, {""first"": 3, ""second"": 22}]}, {""name"": ""c"", ""innerArray"": [{""first"": 1, ""second"": 1}, {""first"": 2, ""second"": 2}]} ]"); // 合并外层数组 var mergedResult = MergeOuterArrays(o1, o2); // 输出格式化后的JSON Console.WriteLine(mergedResult.ToString(Formatting.Indented)); } private static JArray MergeOuterArrays(JArray array1, JArray array2) { // 将外层数组转为以name为键的字典 var outerDict = array1.ToDictionary( item => (string)item["name"], item => (JObject)item.DeepClone() // 深克隆避免修改原数组 ); foreach (JObject item in array2) { string name = (string)item["name"]; if (outerDict.ContainsKey(name)) { // 合并当前name对应的innerArray var inner1 = (JArray)outerDict[name]["innerArray"]; var inner2 = (JArray)item["innerArray"]; outerDict[name]["innerArray"] = MergeInnerArrays(inner1, inner2); } else { // 添加新的对象到结果中 outerDict[name] = (JObject)item.DeepClone(); } } // 按name排序后转为JArray,保证输出顺序和期望一致 return new JArray(outerDict.OrderBy(kv => kv.Key).Select(kv => kv.Value)); } private static JArray MergeInnerArrays(JArray inner1, JArray inner2) { // 将innerArray转为以first为键的字典 var innerDict = inner1.ToDictionary( item => (int)item["first"], item => (JObject)item.DeepClone() ); foreach (JObject item in inner2) { int firstKey = (int)item["first"]; if (innerDict.ContainsKey(firstKey)) { // 更新second值(新值覆盖旧值) innerDict[firstKey]["second"] = item["second"]; } else { // 添加新的元素 innerDict[firstKey] = (JObject)item.DeepClone(); } } // 按first排序后转为JArray return new JArray(innerDict.OrderBy(kv => kv.Key).Select(kv => kv.Value)); } }
3. 代码说明
- 使用
DeepClone()是为了避免修改原始的JArray对象,保证原数据的完整性; - 外层和内层分别通过
name和first作为唯一标识,确保匹配正确的元素; - 最后通过
OrderBy保证输出的数组顺序和你期望的一致(a→b→c,innerArray按first升序)。
关于自定义合并匹配索引的说明
Newtonsoft.Json的默认MergeArrayHandling系列选项,都是基于数组的位置索引来处理合并的,没有内置的方式让你自定义“按对象属性作为匹配索引”。如果需要这种基于业务标识的合并,只能通过手动转换字典的方式实现,或者自定义JsonConverter来扩展合并逻辑,但前者更直观易维护。
内容的提问来源于stack exchange,提问作者Grzegorz
相关产品推荐
相关产品推荐

