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

C#使用System.Text.Json反序列化PagedResult返回默认值问题

问题描述

测试System.Text.Json的序列化与反序列化功能时,反序列化得到的PagedResult对象仅包含默认值,导致测试失败。已实现PagedResultBase抽象类、PagedResult<T>泛型类、UserDtoTest类及测试代码,且必须使用System.Text.Json.Serialization。目前发现两种可能的解决方案:一是为每个属性添加[JsonPropertyName("currentPage")]这类特性;二是添加驼峰命名选项。

问题原因
  1. 命名规则不匹配:类中属性采用PascalCase命名(如CurrentPage),而JSON字符串中使用camelCase命名(如currentPage)。默认情况下,System.Text.Json不会自动映射两种命名规则的属性。
  2. 只读属性无法反序列化:PagedResultBase中的属性只有get访问器,没有set访问器。System.Text.Json默认不会对只读属性进行反序列化赋值。
  3. 计算属性冗余:IsEmpty和IsNotEmpty是依赖Items推导的计算属性,但JSON中包含这两个字段。反序列化时无法为计算属性赋值,且序列化时默认不会输出这类无set访问器的计算属性,导致序列化后的JSON与原始JSON不一致。
解决方案

方案一:显式指定属性映射特性

为每个属性添加[JsonPropertyName]特性匹配JSON字段名,同时通过构造函数让System.Text.Json能反序列化只读属性:

  1. 修改PagedResultBase类:
public abstract class PagedResultBase
{
    [JsonPropertyName("currentPage")]
    public int CurrentPage { get; }
    [JsonPropertyName("resultsPerPage")]
    public int ResultsPerPage { get; }
    [JsonPropertyName("totalPages")]
    public int TotalPages { get; }
    [JsonPropertyName("totalResults")]
    public long TotalResults { get; }

    // 标记构造函数用于反序列化,参数名需与JSON字段名匹配
    [JsonConstructor]
    protected PagedResultBase(int currentPage, int resultsPerPage, int totalPages, long totalResults)
    {
        CurrentPage = currentPage > totalPages ? totalPages : currentPage;
        ResultsPerPage = resultsPerPage;
        TotalPages = totalPages;
        TotalResults = totalResults;
    }

    protected PagedResultBase()
    {
    }
}
  1. 修改PagedResult<T>类的Items属性:
[JsonPropertyName("items")]
public List<T> Items { get; set; }
  1. 移除JSON中的isEmpty和isNotEmpty字段——这两个值由Items推导而来,无需在JSON中存储。

方案二:使用驼峰命名策略

通过JsonSerializerOptions启用驼峰命名映射,同时处理只读属性的反序列化:

  1. 处理只读属性(二选一即可):
    • 方式一:给PagedResultBase的只读属性添加[JsonInclude]特性,允许反序列化只读属性:
    public abstract class PagedResultBase
    {
        [JsonInclude]
        public int CurrentPage { get; }
        [JsonInclude]
        public int ResultsPerPage { get; }
        [JsonInclude]
        public int TotalPages { get; }
        [JsonInclude]
        public long TotalResults { get; }
    
        protected PagedResultBase()
        {
        }
    
        protected PagedResultBase(int currentPage, int resultsPerPage, int totalPages, long totalResults)
        {
            CurrentPage = currentPage > totalPages ? totalPages : currentPage;
            ResultsPerPage = resultsPerPage;
            TotalPages = totalPages;
            TotalResults = totalResults;
        }
    }
    
    • 方式二:确保构造函数参数名与属性名匹配,System.Text.Json会自动通过构造函数为只读属性赋值,无需额外特性。
  2. 修改测试代码中的反序列化逻辑:
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var pagedResult = JsonSerializer.Deserialize<PagedResult<UserDtoTest>>(jsonString, options);
  1. 调整序列化逻辑以匹配原始JSON格式(用于断言):
var serializeOptions = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true // 启用缩进,匹配原始JSON格式
};
var serializedJsonString = JsonSerializer.Serialize(pagedResult, serializeOptions);
  1. 优化断言逻辑:避免直接比较JSON字符串(格式差异会导致失败),改为比较JSON内容的等效性:
var expectedJson = JsonSerializer.Deserialize<JsonElement>(jsonString);
var actualJson = JsonSerializer.Deserialize<JsonElement>(serializedJsonString);
actualJson.Should().BeEquivalentTo(expectedJson);

额外注意点

务必移除JSON中的isEmpty和isNotEmpty字段,否则序列化后的JSON会因缺少这两个字段导致断言失败——这两个值属于派生属性,无需持久化存储。

内容的提问来源于stack exchange,提问作者Kirill Topchy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 14:55:02