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

如何实现序列化含Age属性、反序列化忽略它?现有方案存弊端

针对序列化需求与缓存键生成的优化方案

一、解决序列化/反序列化的优雅方案(无需混用两个序列化库)

你的核心需求是反序列化时忽略Age,序列化时保留Age,完全可以只用System.Text.Json实现,不用同时依赖Newtonsoft:

方法1:自定义Json转换器

写一个针对Request类的转换器,精准控制序列化和反序列化行为:

public class RequestConverter : JsonConverter<Request>
{
    public override Request Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var request = new Request();
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.PropertyName)
            {
                var propName = reader.GetString();
                reader.Read();
                if (propName.Equals(nameof(Request.Name), StringComparison.OrdinalIgnoreCase))
                {
                    request.Name = reader.GetString();
                }
                // 直接跳过Age字段的反序列化
            }
            else if (reader.TokenType == JsonTokenType.EndObject)
            {
                break;
            }
        }
        return request;
    }

    public override void Write(Utf8JsonWriter writer, Request value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WriteString(nameof(Request.Name), value.Name);
        writer.WriteNumber(nameof(Request.Age), value.Age);
        writer.WriteEndObject();
    }
}

使用时给序列化/反序列化指定对应的配置:

var options = new JsonSerializerOptions();
options.Converters.Add(new RequestConverter());

// 反序列化(自动忽略Age)
var request = JsonSerializer.Deserialize<Request>(jsonString, options);

// 序列化(保留Age)
var serialized = JsonSerializer.Serialize(request, options);

方法2:通过元数据配置实现差异化处理

不用写转换器,直接修改JsonSerializerOptions的元数据规则:

// 序列化用默认配置,保留所有属性
var serializeOptions = new JsonSerializerOptions();

// 反序列化配置:忽略Age字段
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
    Modifiers = { typeInfo =>
        {
            if (typeInfo.Type == typeof(Request))
            {
                var ageProp = typeInfo.Properties.First(p => p.Name == nameof(Request.Age));
                ageProp.IsIgnored = true;
            }
        }
    }
};

// 反序列化时用deserializeOptions,序列化用serializeOptions即可

二、缓存键生成的优化方案

你的缓存需求是相同请求内容命中同一缓存,以下几个方案比双序列化器更靠谱:

1. 改用Record类型实现值相等判断

C# 9+的Record默认实现了值相等比较——只要属性值完全相同,两个Record实例就会被判定为相等。把Request改成Record:

public record Request 
{
    public string Name { get; set; }
    
    [JsonIgnore]
    public int Age { get; set; }
}

此时直接用Request实例作为缓存键即可,无需序列化。测试验证:

var req1 = new Request { Name = "Alice", Age = 30 };
var req2 = new Request { Name = "Alice", Age = 30 };
Console.WriteLine(req1.Equals(req2)); // 输出True

主流缓存框架(如MemoryCache、IDistributedCache)都支持基于值比较的键匹配,完全满足需求。

2. 定义业务专属DTO生成缓存键

既然外部服务只关心影响结果的请求字段(看起来Age不影响外部服务响应),可以专门定义一个用于和外部服务交互的DTO:

public class ExternalRequest
{
    public string Name { get; set; }
}

控制器接收Request后,映射为ExternalRequest发送给外部服务,缓存键直接用ExternalRequest的序列化字符串或实例即可。这样彻底避免了Age字段对缓存的干扰,逻辑更清晰。

3. 安全高效的哈希缓存键

如果担心序列化性能,或者必须用字符串键,可以基于业务关键字段生成哈希:

public string GenerateCacheKey(Request request)
{
    // 只使用影响外部服务响应的字段拼接成原始键内容
    var keyContent = $"Name:{request.Name}";
    // 用SHA256生成哈希,碰撞概率极低,性能损失可接受
    using var sha256 = SHA256.Create();
    var bytes = Encoding.UTF8.GetBytes(keyContent);
    var hashBytes = sha256.ComputeHash(bytes);
    return Convert.ToBase64String(hashBytes);
}

MD5在业务场景中碰撞概率几乎为0,如果追求极致性能也可以用MD5替代SHA256。

三、综合最优方案

结合你的场景,推荐优先采用:

  1. 将Request改为Record类型,直接用实例作为缓存键,省去序列化步骤
  2. 用System.Text.Json的元数据配置实现反序列化忽略Age、序列化保留Age的需求
  3. 若Age确实不影响外部服务响应,定义ExternalRequest DTO,进一步简化缓存逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:50:14