.netframework升级为.net8以后元组数据返回格式变成了 [{}]
问题代码
问题描述:MatExpireMoney这个方法前端请求以后得到的返回值是[{}]
public async Task<JsonResult> MatExpireMoney(int platId = 0)
{
if (platId == 0)
{
platId = NowUser.PlatId;
}
var schIds = GetSchIds(platId);
var expireMatMoney = storageRepo.Where(r => (r.Qty1 > 0 || r.Qty2 > 0) && schIds.Contains(r.SchoolId) && r.ExpirDate < nowDate).Sum(r => (decimal?)(r.Price * (r.Qty1 + r.Qty2))) ?? 0;
var value = PriceConvert(expireMatMoney);
return Json(value);
}
public List<(decimal Money, string Unit)> PriceConvert(decimal? price)
{
var unit = "";
if (price > 0)
{
if (price > 100000000)
{
price = (decimal?)Math.Round((double)(price / 100000000), 2);
unit = "亿";
}
else if (price > 10000)
{
price = (decimal?)Math.Round((double)(price / 10000), 2);
unit = "万";
}
else
{
price = price;
unit = "元";
}
}
else
{
price = 0;
unit = "元";
}
List<(decimal Money, string Unit)> model = new List<(decimal Money, string Unit)>();
model.Add(((decimal)price, unit));
return model;
}
原因
从 .NET Framework 升级到 .NET 8 后,返回的数据格式变成了 [{}],这通常是因为 .NET Core 及更高版本(包括 .NET 8)对 JSON 序列化的默认行为发生了变化
-
默认序列化器变化
在 .NET Framework 中,默认使用的是 Newtonsoft.Json(即 Json.NET)进行 JSON 序列化。
在 .NET Core 及更高版本中,默认使用的是 System.Text.Json,它的行为与 Newtonsoft.Json 有所不同。 -
System.Text.Json 的行为差异
System.Text.Json 对匿名类型、值类型和集合的序列化行为与 Newtonsoft.Json 不同。
例如,System.Text.Json 会将 List<(decimal Money, string Unit)> 序列化为 [{}],因为它无法直接处理元组类型的集合。 -
返回类型问题
在 .NET Framework 中,JsonResult 可能对返回值进行了隐式处理,而在 .NET 8 中,JsonResult 的行为更加严格。
解决方案
方案 1:修改返回类型
将 List<(decimal Money, string Unit)> 改为一个明确的类类型,System.Text.Json 可以更好地处理类类型的序列化。
public class MoneyModel
{
public decimal Money { get; set; }
public string Unit { get; set; }
}
public List<MoneyModel> PriceConvert(decimal? price)
{
var unit = "";
if (price > 0)
{
if (price > 100000000)
{
price = (decimal?)Math.Round((double)(price / 100000000), 2);
unit = "亿";
}
else if (price > 10000)
{
price = (decimal?)Math.Round((double)(price / 10000), 2);
unit = "万";
}
else
{
price = price;
unit = "元";
}
}
else
{
price = 0;
unit = "元";
}
return new List<MoneyModel>
{
new MoneyModel { Money = (decimal)price, Unit = unit }
};
}
然后在 MatExpireMoney 方法中直接返回 List:
public async Task<JsonResult> MatExpireMoney(int platId = 0)
{
if (platId == 0)
{
platId = NowUser.PlatId;
}
if (platId == 155) // 航空学院 特殊处理
{
var path = "/135/screen/new/timeout/r.do";
var hk_Value = await HK_GetRequest<List<HK_MoneyModel>>(platId, path, "食材过期金额");
return Json(hk_Value);
}
var schIds = GetSchIds(platId);
var expireMatMoney = storageRepo
.Where(r => (r.Qty1 > 0 || r.Qty2 > 0) && schIds.Contains(r.SchoolId) && r.ExpirDate < nowDate)
.Sum(r => (decimal?)(r.Price * (r.Qty1 + r.Qty2))) ?? 0;
var value = PriceConvert(expireMatMoney);
return Json(value);
}
方案 2:配置 System.Text.Json 的序列化选项
如果不想修改返回类型,可以通过配置 System.Text.Json 的序列化选项来支持元组类型的序列化。
在 Startup.cs 或 Program.cs 中配置 JSON 选项:
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null; // 保持属性名称大小写
options.JsonSerializerOptions.Converters.Add(new TupleConverter()); // 添加自定义元组转换器
});
然后实现一个自定义的 JsonConverter 来处理元组类型:
public class TupleConverter : JsonConverter<List<(decimal Money, string Unit)>>
{
public override List<(decimal Money, string Unit)> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, List<(decimal Money, string Unit)> value, JsonSerializerOptions options)
{
writer.WriteStartArray();
foreach (var item in value)
{
writer.WriteStartObject();
writer.WriteNumber("Money", item.Money);
writer.WriteString("Unit", item.Unit);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
}
方案3:方案 3:回退到 Newtonsoft.Json
如果不想修改代码逻辑,可以回退到 Newtonsoft.Json,这是 .NET Framework 中默认的 JSON 序列化库。
安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson 包:
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
在 Startup.cs 或 Program.cs 中配置:
builder.Services.AddControllers()
.AddNewtonsoftJson(); // 使用 Newtonsoft.Json 作为默认序列化器
总结
推荐方案 1:修改返回类型为明确的类类型,这是最规范和可维护的方式。
方案 2:适合需要保留元组类型的场景,但需要额外实现自定义转换器。
方案 3:适合快速迁移,但长期来看不建议依赖 Newtonsoft.Json。