You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多态结果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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:48:12