如何在ASP.NET Web API C#中返回指定格式的JSON数组
解决ASP.NET Web API返回嵌套JSON结构的问题
看起来你的核心问题是:EF自动生成的存储过程结果类是扁平结构,直接返回List会输出扁平的JSON数组,但你需要的是多层嵌套的目标结构。下面是一步步的解决方案:
1. 创建匹配目标JSON的DTO类
首先,我们需要定义和目标JSON结构完全对应的数据传输对象(DTO),因为EF生成的实体类无法直接映射嵌套结构:
// 最内层的子区域明细 public class SubDistDetail { public string SubDistBbsID { get; set; } public string Amount { get; set; } // 匹配目标的字符串类型 public string Hail { get; set; } public string Date { get; set; } } // 区域层级明细 public class DistDetail { public string DistBbsID { get; set; } public string SubDistCount { get; set; } public string Amount { get; set; } public string Hail { get; set; } public List<SubDistDetail> DistDetails { get; set; } } // 大区域汇总层级 public class AreaDetail { public string AreaBbsID { get; set; } public string DistCount { get; set; } public string SubDistCount { get; set; } public string Amount { get; set; } public string Hail { get; set; } public List<DistDetail> ArealDetails { get; set; } } // 最外层根对象,匹配目标的JSON结构 public class RainfallAreaAVGResponse { public List<AreaDetail> RainfallAreaAVG { get; set; } }
2. 修改控制器方法,组装嵌套数据
接下来,需要修改控制器方法,同时调用两个存储过程获取汇总和明细数据,通过LINQ分组、投影把扁平数据转换成嵌套结构。另外要解决原来两个GetRainfall方法的路由冲突问题:
namespace RainfallService.Controllers { public class DistAVGController : ApiController { // 返回嵌套的目标JSON结构 [HttpGet] [Route("api/DistAVG/RainfallSummary")] // 自定义路由避免方法冲突 public RainfallAreaAVGResponse GetRainfallSummary(string distBbsID, string entryDate) { using (var db = new Farmer_WebEntities()) { // 获取汇总数据 var summaryData = db.SP_GetRainfallByDistDateAVG(distBbsID, entryDate).ToList(); // 获取明细数据 var detailData = db.SP_GetRainfallByDistDateAVGDetails(distBbsID, entryDate).ToList(); // 组装嵌套结构 var response = new RainfallAreaAVGResponse { RainfallAreaAVG = summaryData .GroupBy(s => s.AreaBbsId) // 按大区域分组 .Select(areaGroup => new AreaDetail { AreaBbsID = areaGroup.Key, DistCount = areaGroup.Select(s => s.DistBbsID).Distinct().Count().ToString(), SubDistCount = areaGroup.Sum(s => s.SubDistCount ?? 0).ToString(), Amount = areaGroup.Sum(s => s.Amount ?? 0).ToString(), Hail = areaGroup.Sum(s => s.Hail ?? 0).ToString(), ArealDetails = areaGroup .GroupBy(s => s.DistBbsID) // 按区域分组 .Select(distGroup => new DistDetail { DistBbsID = distGroup.Key, SubDistCount = distGroup.Sum(s => s.SubDistCount ?? 0).ToString(), Amount = distGroup.Sum(s => s.Amount ?? 0).ToString(), Hail = distGroup.Sum(s => s.Hail ?? 0).ToString(), DistDetails = detailData .Where(d => d.AreaBbsId == areaGroup.Key && d.DistBbsID == distGroup.Key) .Select(d => new SubDistDetail { SubDistBbsID = d.SubDistBbsId, Amount = (d.Amount ?? 0).ToString(), Hail = (d.Hail ?? 0).ToString(), Date = entryDate // 用传入的entryDate填充Date字段 }).ToList() }).ToList() }).ToList() }; return response; } } // 保留原有的明细接口(如果需要),修正路由和参数 [HttpGet] [Route("api/DistAVG/RainfallDetails")] public List<SP_GetRainfallByDistDateAVGDetails_Result> GetRainfallDetails(string distBbsID, string entryDate, string type) { using (var db = new Farmer_WebEntities()) { // 注意:原代码未使用type参数,若存储过程需要该参数,请补充到调用中 var rainfalls = db.SP_GetRainfallByDistDateAVGDetails(distBbsID, entryDate).ToList(); return rainfalls; } } } }
3. 调整Web API序列化设置(可选)
如果你的目标JSON属性名是大驼峰格式(比如AreaBbsID),上面的DTO已经用了和目标一致的命名,序列化会自动匹配。若需要更灵活的命名控制,可以在WebApiConfig里调整:
// 保留属性原有的大小写格式(匹配目标的大驼峰) config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver(); // 若需要驼峰命名可启用下面的代码,但你的目标是大驼峰,所以不需要 // config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
为什么原来的代码不行?
你之前直接返回EF生成的List<SP_GetRainfallByDistDateAVG_Result>,这些类是扁平结构,Web API会直接将其序列化为扁平的JSON数组;而你需要的是多层嵌套的层级结构,所以必须手动将扁平数据组装成嵌套的DTO对象才能输出目标格式。
内容的提问来源于stack exchange,提问作者sydur.rahman21
相关产品推荐
相关产品推荐

