ASP.NET Core中HttpGet方法自定义对象序列化失败问题
问题:ASP.NET Core MVC + MongoDB API返回JSON时Schedule字段为空
我正在开发基于ASP.NET Core MVC和MongoDB的小型API,参考微软官方文档实现,但遇到HttpGet请求返回时对象序列化异常的问题:从MongoDB读取的Group对象数据正常,但返回的JSON中schedule字段为空(实际应包含Weekday列表)。
模型代码
[Serializable] public class Group { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } public string groupName { get; set; } public Schedule schedule { get; set; } public Group() { groupName = "UNKNOWN"; schedule = new Schedule(); } public Group(string groupName) { this.groupName = groupName; schedule = new Schedule(); } } [Serializable] public class Schedule { public List<Weekday> week = new List<Weekday>(); } [Serializable] public class Weekday { public List<DaysSchedule> daysSchedules { get; set; } = new List<DaysSchedule>(); public int dayNumber { get; set; } = 0; } public class DaysSchedule { public List<string> dates = new List<string>(); public List<Classes> classes = new List<Classes>(); } public class Classes { [BsonElement("ordinal")] public int ordinal { get; set; } [BsonElement("name")] public string name { get; set; } [BsonElement("teacher")] public string? teacher { get; set; } [BsonElement("type")] public string type { get; set; } [BsonElement("location")] public string location { get; set; } public Classes() { ordinal = 0; name = "UNKNOWN"; type = "UNDEFINED"; location = "UNDEFINED"; } public Classes(int ordinal, string name, string teacher, string type, string location) { this.ordinal = ordinal; this.name = name; this.teacher = teacher; this.type = type; this.location = location; } }
期望返回的JSON
{ "Id": "63ea85829903ec2ab03720d3", "groupName": "М3О-225Бк-21", "schedule": { "week": [ { "daysSchedules": [ { "dates": [ "13.02.2023", "13.02.2023" ], "classes": [ { "ordinal": 0, "name": "Math", "teacher": "Lyahner", "type": "PZ", "location": "64" } ] } ], "dayNumber": 0 } ] } }
实际返回的JSON
{ "id": "63ea85829903ec2ab03720d3", "groupName": "М3О-225Бк-21", "schedule": {} }
控制器代码
using Newtonsoft.Json; using Microsoft.AspNetCore.Mvc; using ThreeplyWebApi.Services; using ThreeplyWebApi.Models; using System.Text.Json; namespace ThreeplyWebApi.Controllers { [ApiController] [Route("controller")] public class GroupsController : ControllerBase { readonly private GroupsService _groupsService; public GroupsController(GroupsService schedulesService) { _groupsService = schedulesService; } [HttpGet] public async Task<List<Group>> Get() => await _groupsService.GetAsync(); [HttpGet("{groupName}")] public async Task<ActionResult<Group>> Get(string groupName) { var group = await _groupsService.GetAsync(groupName); if (group == null) { return NotFound(); } return Ok(group); } [HttpPost] public async Task<IActionResult> Post([FromBody]Group newGroup) { await _groupsService.CreateAsync(newGroup); return CreatedAtAction(nameof(Get), new { groupName = newGroup.groupName }, newGroup); } } }
数据库服务代码
namespace ThreeplyWebApi.Services { public class GroupsService { private readonly IMongoCollection<Group> _groupsCollection; public GroupsService(IOptions<GroupsDatabaseSettings> groupDatabaseSettings) { var MongoClient = new MongoClient(groupDatabaseSettings.Value.ConnectionString); var MongoDatabase = MongoClient.GetDatabase(groupDatabaseSettings.Value.DatabaseName); _groupsCollection = MongoDatabase.GetCollection<Group>(groupDatabaseSettings.Value.GroupsCollectionName); } public async Task<List<Group>> GetAsync() { return await _groupsCollection.Find<Group>(_ => true).ToListAsync(); } public async Task<Group> GetAsync(string groupName) { return await _groupsCollection.Find(x => x.groupName == groupName).FirstOrDefaultAsync(); } public async Task CreateAsync(Group group) { await _groupsCollection.InsertOneAsync(group); } public async Task UpdateAsync(string groupName, Group updatedGroup) => await _groupsCollection.ReplaceOneAsync(x => x.groupName == groupName, updatedGroup); public async Task RemoveAsync(string groupName) => await _groupsCollection.DeleteOneAsync(x => x.groupName == groupName); } }
疑问
不清楚HttpGet/HttpPost属性的序列化时机,尝试用Newtonsoft.Json显式序列化可行,但希望找到规范的解决方案,同时想了解如何为Group对象配置自定义序列化,以便在带路由属性的方法中直接返回。
解决方案
核心问题:字段未被序列化
ASP.NET Core默认使用System.Text.Json进行序列化,而System.Text.Json只会序列化公共属性(Property),不会序列化公共字段(Field)。你的Schedule类中的week、DaysSchedule中的dates和classes都是字段,不是属性,所以序列化时被忽略了。
步骤1:将字段改为属性
把模型中的字段替换为带getter/setter的属性:
[Serializable] public class Schedule { // 改为属性 public List<Weekday> week { get; set; } = new List<Weekday>(); } public class DaysSchedule { // 改为属性 public List<string> dates { get; set; } = new List<string>(); public List<Classes> classes { get; set; } = new List<Classes>(); }
步骤2:(可选)统一使用Newtonsoft.Json
如果你习惯使用Newtonsoft.Json(Json.NET)而非默认的System.Text.Json,可以全局替换序列化器:
- 安装NuGet包:
Microsoft.AspNetCore.Mvc.NewtonsoftJson - 在
Program.cs中配置:
builder.Services.AddControllers() .AddNewtonsoftJson(options => { // 可添加自定义配置,比如保留原字段名、忽略空值等 options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver(); });
步骤3:自定义序列化规则(按需)
针对System.Text.Json:
用JsonPropertyName指定JSON字段名,或实现自定义转换器:
// 指定字段名示例 public class Group { [JsonPropertyName("group_id")] [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } } // 自定义转换器示例 public class GroupConverter : JsonConverter<Group> { public override Group Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 自定义反序列化逻辑 using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; return new Group { Id = root.GetProperty("group_id").GetString(), groupName = root.GetProperty("groupName").GetString() ?? "UNKNOWN" }; } public override void Write(Utf8JsonWriter writer, Group value, JsonSerializerOptions options) { // 自定义序列化逻辑 writer.WriteStartObject(); writer.WriteString("group_id", value.Id); writer.WriteString("groupName", value.groupName); JsonSerializer.Serialize(writer, value.schedule, options); writer.WriteEndObject(); } } // 注册转换器 builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new GroupConverter()); });
针对Newtonsoft.Json:
用JsonProperty指定字段名,或实现自定义转换器:
// 指定字段名示例 public class Group { [JsonProperty("group_id")] [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } } // 自定义转换器示例 public class GroupConverter : Newtonsoft.Json.JsonConverter<Group> { public override void WriteJson(JsonWriter writer, Group value, Newtonsoft.Json.JsonSerializer serializer) { writer.WriteStartObject(); writer.WritePropertyName("group_id"); serializer.Serialize(writer, value.Id); writer.WritePropertyName("groupName"); serializer.Serialize(writer, value.groupName); writer.WritePropertyName("schedule"); serializer.Serialize(writer, value.schedule); writer.WriteEndObject(); } public override Group ReadJson(JsonReader reader, Type objectType, Group existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer) { var obj = JObject.Load(reader); return new Group { Id = obj["group_id"]?.ToString(), groupName = obj["groupName"]?.ToString() ?? "UNKNOWN", schedule = obj["schedule"]?.ToObject<Schedule>(serializer) ?? new Schedule() }; } } // 全局注册或在类上标记[JsonConverter(typeof(GroupConverter))]
序列化时机说明
- 控制器返回
Ok(object)或直接返回对象时,响应准备阶段会自动序列化对象为JSON响应体; HttpPost的[FromBody]会在请求接收阶段自动反序列化请求体为对象。
内容的提问来源于stack exchange,提问作者Wayne301
相关产品推荐
相关产品推荐

