多态结果JSON反序列化:分页API结果合并的实现难题
问题描述
我需要调用两个RESTful API,它们返回的结构除items字段外完全一致,API返回示例如下:
{ "hasmoredata":true, "current_page": 1, "page_size": 20, "total_pages": 5, "items": [ { "user_id": "1", "username": "carl", "first_name": "carl" } ] } { "hasmoredata":true, "current_page": 1, "page_size": 10, "total_pages": 2, "items": [ { "course_id": "10", "course_name": "Math", "duration": "3h" } ] }
我希望通过C#抽象类CursorResult封装公共属性,再用UsersList和CoursesList两个子类分别对应不同的items结构,代码如下:
public abstract class CursorResult { [JsonProperty("current_page")] public int CurrentPage { get; set; } [JsonProperty("page_size")] public int PageSize { get; set; } [JsonProperty("total_pages")] public int TotalPages { get; set; } [JsonProperty("hasmoredata")] public bool HasMoreData{ get; set; } } public class UsersList : CursorResult { [JsonProperty("items")] List<User> Users { get; set; } } public class CoursesList : CursorResult { [JsonProperty("items")] List<Courses> Courses { get; set; } }
现在需要实现GetEntireResult函数以获取所有分页结果并合并,但不知如何编写AddItems方法,使其能根据API结果自动添加Users或Courses数据,恳请帮助。
解决方案
咱们可以通过泛型抽象类重构你的基础结构,这样既能保留公共分页属性,又能让子类自动适配不同的items类型,同时让AddItems方法具备通用性。以下是具体实现步骤:
1. 重构泛型抽象类
首先把CursorResult改成泛型抽象类,将items字段抽象为泛型列表,同时添加一个抽象方法让子类提供自身的items集合:
public abstract class CursorResult<TItem> { [JsonProperty("current_page")] public int CurrentPage { get; set; } [JsonProperty("page_size")] public int PageSize { get; set; } [JsonProperty("total_pages")] public int TotalPages { get; set; } [JsonProperty("hasmoredata")] public bool HasMoreData { get; set; } // 抽象方法,让子类返回自身的items集合 public abstract List<TItem> GetItems(); }
2. 修改子类实现
让UsersList和CoursesList继承泛型抽象类,并实现GetItems方法:
public class UsersList : CursorResult<User> { [JsonProperty("items")] public List<User> Users { get; set; } = new List<User>(); public override List<User> GetItems() { return Users; } } public class CoursesList : CursorResult<Course> { [JsonProperty("items")] public List<Course> Courses { get; set; } = new List<Course>(); public override List<Course> GetItems() { return Courses; } } // 补充对应的实体类(假设你已经有了,这里列出来更完整) public class User { [JsonProperty("user_id")] public string UserId { get; set; } [JsonProperty("username")] public string Username { get; set; } [JsonProperty("first_name")] public string FirstName { get; set; } } public class Course { [JsonProperty("course_id")] public string CourseId { get; set; } [JsonProperty("course_name")] public string CourseName { get; set; } [JsonProperty("duration")] public string Duration { get; set; } }
3. 实现通用的GetEntireResult和AddItems方法
现在可以编写一个通用的方法来获取所有分页数据,AddItems逻辑也能自动适配不同类型:
public async Task<TResult> GetEntireResult<TResult, TItem>(Func<int, Task<TResult>> fetchPage) where TResult : CursorResult<TItem>, new() { var entireResult = new TResult(); int currentPage = 1; do { var pageResult = await fetchPage(currentPage); // 调用AddItems合并数据 AddItems(entireResult.GetItems(), pageResult.GetItems()); // 更新分页信息(这里可以根据需求调整,比如保留最大的总页数等) entireResult.TotalPages = pageResult.TotalPages; entireResult.HasMoreData = pageResult.HasMoreData; entireResult.PageSize = pageResult.PageSize; currentPage++; } while (entireResult.HasMoreData && currentPage <= entireResult.TotalPages); return entireResult; } // 通用的AddItems方法,负责合并两个列表 private void AddItems<TItem>(List<TItem> targetList, List<TItem> sourceList) { if (sourceList == null || !sourceList.Any()) return; targetList.AddRange(sourceList); }
4. 使用示例
调用的时候只需要传入获取单页数据的委托即可,比如获取所有用户:
var allUsers = await GetEntireResult<UsersList, User>(async page => { // 这里替换成你调用用户API的逻辑,比如用HttpClient var response = await _httpClient.GetFromJsonAsync<UsersList>($"api/users?page={page}"); return response; }); // 获取所有课程同理 var allCourses = await GetEntireResult<CoursesList, Course>(async page => { var response = await _httpClient.GetFromJsonAsync<CoursesList>($"api/courses?page={page}"); return response; });
这个方案的好处是:
- 完全复用了分页逻辑,不需要为每个类型写重复的
GetEntireResult和AddItems - 通过泛型和抽象方法,自动适配不同的
items类型,不用手动判断类型 - 代码结构清晰,扩展性强,后续如果新增其他分页API,只需要继承
CursorResult<TItem>即可
内容的提问来源于stack exchange,提问作者Lapo
相关产品推荐
相关产品推荐

