基于动态JSON字段列表在C#中实现数据分组与聚合
C# 动态JSON分组聚合解决方案
针对你需要对动态结构的JSON数组进行类似SQL分组聚合的需求,这里提供基于**Newtonsoft.Json(Json.NET)**的实现方案,该库在处理动态JSON场景下灵活性极强,能满足指定分组字段+聚合函数(SUM/MIN/MAX/COUNT)的需求。
核心实现思路
- 解析输入JSON数组为
JArray,方便动态访问每个JSON对象的属性 - 解析分组字段列表,区分普通分组字段(如
col1)和聚合字段(如MAX(col3)) - 以普通分组字段的组合值作为分组键,对JSON对象分组
- 对每个分组应用对应的聚合函数计算结果
- 将分组键与聚合结果组合成新的JSON对象,最终输出聚合后的JSON数组
完整代码示例
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; public class JsonGroupAggregator { public static string AggregateJson(string inputJson, List<string> groupFields) { // 解析输入JSON数组 JArray inputArray = JArray.Parse(inputJson); if (!inputArray.Any()) return "[]"; // 拆分分组字段和聚合字段 var groupingKeys = new List<string>(); var aggregations = new List<(string FuncName, string FieldName)>(); foreach (var field in groupFields) { if (field.StartsWith("SUM(") || field.StartsWith("MIN(") || field.StartsWith("MAX(") || field.StartsWith("COUNT(")) { // 提取聚合函数和目标字段 var funcEndIndex = field.IndexOf('('); var fieldEndIndex = field.IndexOf(')'); string funcName = field.Substring(0, funcEndIndex); string fieldName = field.Substring(funcEndIndex + 1, fieldEndIndex - funcEndIndex - 1); aggregations.Add((funcName, fieldName)); } else { groupingKeys.Add(field); } } // 分组逻辑:以分组字段的键值对组合作为分组依据 var groupedData = inputArray.GroupBy(item => { var keyValues = groupingKeys.ToDictionary(key => key, key => item[key]); return new Dictionary<string, JToken>(keyValues); }, new DictionaryComparer()); // 生成聚合结果 var resultArray = new JArray(); foreach (var group in groupedData) { var resultObj = new JObject(); // 添加分组字段到结果 foreach (var key in groupingKeys) { resultObj[key] = group.Key[key]; } // 处理聚合函数 foreach (var (funcName, fieldName) in aggregations) { var fieldValues = group.Select(item => item[fieldName]); JToken aggregatedValue = funcName switch { "SUM" => new JValue(fieldValues.Sum(v => v.ToObject<decimal>())), "MIN" => fieldValues.Min(), "MAX" => fieldValues.Max(), "COUNT" => new JValue(group.Count()), _ => throw new NotSupportedException($"不支持的聚合函数:{funcName}") }; resultObj[$"{funcName}({fieldName})"] = aggregatedValue; } resultArray.Add(resultObj); } return resultArray.ToString(Formatting.Indented); } // 自定义字典比较器,用于GroupBy的分组键比较 private class DictionaryComparer : IEqualityComparer<Dictionary<string, JToken>> { public bool Equals(Dictionary<string, JToken> x, Dictionary<string, JToken> y) { if (x.Count != y.Count) return false; foreach (var pair in x) { if (!y.ContainsKey(pair.Key) || !JToken.DeepEquals(pair.Value, y[pair.Key])) { return false; } } return true; } public int GetHashCode(Dictionary<string, JToken> obj) { int hash = 17; foreach (var pair in obj.OrderBy(kv => kv.Key)) { hash = hash * 31 + pair.Key.GetHashCode(); hash = hash * 31 + (pair.Value?.GetHashCode() ?? 0); } return hash; } } } // 使用示例 public class Program { public static void Main() { string inputJson = @"[{""col1"": ""a1"",""col2"": ""b1"",""col3"": 10}, {""col1"": ""a1"",""col2"": ""b1"",""col3"": 11}, {""col1"": ""a2"",""col2"": ""b2"",""col3"": 5}, {""col1"": ""a2"",""col2"": ""b2"",""col3"": 7}]"; List<string> groupFields = new List<string> { "col1", "col2", "MAX(col3)", "SUM(col3)" }; string result = JsonGroupAggregator.AggregateJson(inputJson, groupFields); Console.WriteLine(result); } }
代码说明
- 分组键处理:通过自定义
DictionaryComparer确保不同JSON对象的分组字段值完全一致时被分到同一组 - 聚合函数支持:当前实现了SUM(按decimal计算,兼容整数和浮点数)、MIN、MAX、COUNT四种常用聚合函数,可根据需求扩展
- 动态适配:无需提前定义实体类,完全基于JSON的动态结构处理,适配任意字段组合
- 结果格式:输出的JSON保持与输入一致的字段命名规则,聚合结果字段名保留
Func(Field)格式(如MAX(col3))
注意事项
- 确保输入JSON中的聚合字段为数值类型(SUM/MIN/MAX),否则会抛出类型转换异常
- 如果需要支持更多聚合函数(如AVG),只需在
aggregatedValue的switch语句中添加对应逻辑 - 若使用
System.Text.Json,可将JArray/JObject替换为JsonElement,核心逻辑保持一致,只是动态属性访问方式略有不同
内容的提问来源于stack exchange,提问作者Chethan SR
相关产品推荐
相关产品推荐

