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

使用System.Text.Json源生成功能时,如何添加序列化修饰器实现属性按字母序排序?

使用System.Text.Json源生成功能时,如何添加序列化修饰器实现属性按字母序排序?

我完全懂你现在的困扰——之前靠反射模式里的TypeInfoResolver修饰器轻松实现了属性排序,结果转源生成配合NativeAOT/修剪的时候,发现这套逻辑完全插不进去,还踩了好几个坑对吧?别慌,确实不用重写整个源生成器就能解决,我来给你捋捋可行的方案。

先说说为什么你之前的尝试卡壳了

  1. JsonSourceGenerationOptions确实没开放修饰器入口:源生成的核心配置都是编译时确定的,属性里没留加修饰器的口子很正常。
  2. 循环依赖的死胡同:你想先用Options初始化Context,再用Context修改Options,这本身就是鸡生蛋的问题——源生成的JsonSerializerContext.Default是编译时静态生成的,根本不需要你手动初始化,之前绕错方向了。
  3. 只读的JsonTypeInfo:源生成的TypeInfo是编译时固化的,标记为只读,防止修改后破坏缓存,所以直接改肯定报错。

可行方案:自定义IJsonTypeInfoResolver组合源生成逻辑和排序

核心思路是:不碰源生成的只读TypeInfo,而是用自定义Resolver把源生成的Resolver和你的排序逻辑“拼”起来——先从源生成器拿TypeInfo,复制成可写的副本,排序后再用这个副本。既保留源生成的性能,又实现了排序,还完全支持修剪和NativeAOT。

步骤1:保留你的源生成Context

先继续用原来的源生成配置,比如:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(Order))]
// 把你需要序列化的类型都加上
public partial class AppJsonContext : JsonSerializerContext
{
}

步骤2:复用你的排序修饰逻辑

原来的AlphabetizeProperties可以直接用,不用改:

private static void AlphabetizeProperties(JsonTypeInfo info)
{
    if (info.Kind != JsonTypeInfoKind.Object) return;

    var sortedProperties = info.Properties
        .OrderBy(p => p.Name, StringComparer.Ordinal)
        .ToList();

    info.Properties.Clear();
    for (int i = 0; i < sortedProperties.Count; i++)
    {
        sortedProperties[i].Order = i;
        info.Properties.Add(sortedProperties[i]);
    }
}

步骤3:实现自定义Resolver组合逻辑

写一个轻量的自定义Resolver,它会先从源生成Context拿TypeInfo,复制成可写副本后再排序:

public class SortingTypeInfoResolver : IJsonTypeInfoResolver
{
    private readonly IJsonTypeInfoResolver _sourceGenResolver;

    public SortingTypeInfoResolver(IJsonTypeInfoResolver sourceGenResolver)
    {
        _sourceGenResolver = sourceGenResolver;
    }

    public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
    {
        // 从源生成器拿只读的TypeInfo
        var generatedTypeInfo = _sourceGenResolver.GetTypeInfo(type, options);
        if (generatedTypeInfo == null || generatedTypeInfo.Kind != JsonTypeInfoKind.Object)
        {
            return generatedTypeInfo;
        }

        // 复制成可写的TypeInfo副本
        var writableTypeInfo = JsonTypeInfo.CreateJsonTypeInfo(type, options);
        foreach (var prop in generatedTypeInfo.Properties)
        {
            writableTypeInfo.Properties.Add(prop);
        }

        // 应用排序
        AlphabetizeProperties(writableTypeInfo);
        return writableTypeInfo;
    }
}

步骤4:创建全局的JsonSerializerOptions

最后把自定义Resolver挂到Options上,全局复用:

public static class JsonSettings
{
    public static readonly JsonSerializerOptions Options = new()
    {
        WriteIndented = true,
        // 组合源生成Resolver和排序逻辑
        TypeInfoResolver = new SortingTypeInfoResolver(AppJsonContext.Default)
    };
}

步骤5:正常序列化

直接用这个Options序列化就好,属性会自动按字母序排列:

var user = new User { Name = "Alice", Age = 30, Email = "alice@example.com" };
var json = JsonSerializer.Serialize(user, JsonSettings.Options);
// 输出的JSON属性会按 Age → Email → Name 的顺序排列

为什么这个方案能行?

  • 无反射、全源生成:所有TypeInfo都是编译时生成的,自定义Resolver只是做了复制和排序,没有用反射,完全支持修剪和NativeAOT。
  • 无循环依赖:直接用编译好的AppJsonContext.Default(静态实例)初始化Resolver,根本不需要手动创建Context,自然没有循环依赖问题。
  • 逻辑兼容:排序逻辑和你之前的完全一致,不会引入新的行为差异。

如果想更简洁,还可以把自定义Resolver改成通用的委托式实现,省得单独写类:

// 通用的委托式Resolver
public class DelegatingJsonResolver : IJsonTypeInfoResolver
{
    private readonly IJsonTypeInfoResolver _innerResolver;
    public Func<JsonTypeInfo, JsonTypeInfo>? PostProcess { get; set; }

    public DelegatingJsonResolver(IJsonTypeInfoResolver innerResolver)
    {
        _innerResolver = innerResolver;
    }

    public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options)
    {
        var info = _innerResolver.GetTypeInfo(type, options);
        return info != null ? PostProcess?.Invoke(info) ?? info : null;
    }
}

// 简化Options初始化
public static class JsonSettings
{
    public static readonly JsonSerializerOptions Options = new()
    {
        WriteIndented = true,
        TypeInfoResolver = new DelegatingJsonResolver(AppJsonContext.Default)
        {
            PostProcess = info =>
            {
                if (info.Kind != JsonTypeInfoKind.Object) return info;
                
                var writable = JsonTypeInfo.CreateJsonTypeInfo(info.Type, Options);
                foreach (var prop in info.Properties)
                    writable.Properties.Add(prop);
                
                AlphabetizeProperties(writable);
                return writable;
            }
        }
    };
}

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 06:53:00