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

如何使Utf8Json的NullStringJsonFormatter全局生效无需属性注解?

解决Utf8Json自定义Null字符串格式化器全局生效问题

问题分析

你已经实现了NullStringJsonFormatter,通过属性注解[JsonFormatter(typeof(NullStringJsonFormatter))]可以正常将字符串"null"反序列化为null,但全局注册到CompositeResolver后未生效,而StandardResolver.ExcludeNull功能正常。这通常是因为自定义格式化器的注册顺序或Resolver构建方式不正确,导致StandardResolver的默认string格式化器优先级更高,覆盖了你的自定义实现。

正确实现步骤

1. 确保自定义格式化器逻辑正确

先确认你的NullStringJsonFormatter完整实现了序列化和反序列化逻辑,覆盖所有边界情况:

public class NullStringJsonFormatter : IJsonFormatter<string>
{
    public void Serialize(ref JsonWriter writer, string value, IJsonFormatterResolver resolver)
    {
        if (value == null || value.Equals("null", StringComparison.OrdinalIgnoreCase))
        {
            writer.WriteNull();
            return;
        }
        writer.WriteString(value);
    }

    public string Deserialize(ref JsonReader reader, IJsonFormatterResolver resolver)
    {
        if (reader.GetCurrentJsonToken() == JsonToken.Null)
        {
            reader.ReadNext();
            return null;
        }

        var rawString = reader.ReadString();
        return rawString.Equals("null", StringComparison.OrdinalIgnoreCase) ? null : rawString;
    }
}

2. 构建自定义Resolver并全局注册

要让自定义格式化器全局生效,需要创建一个专门的Resolver来托管它,并确保在CompositeResolver中优先于StandardResolver被调用:

方式一:自定义Resolver类
public class NullStringResolver : IJsonFormatterResolver
{
    // 单例实例避免重复创建
    public static readonly NullStringResolver Instance = new NullStringResolver();

    public IJsonFormatter<T> GetFormatter<T>()
    {
        // 仅对string类型返回自定义格式化器,其他类型交给后续Resolver处理
        return FormatterCache<T>.Formatter;
    }

    // 静态缓存类提升性能
    private static class FormatterCache<T>
    {
        public static readonly IJsonFormatter<T> Formatter = 
            typeof(T) == typeof(string) ? (IJsonFormatter<T>)new NullStringJsonFormatter() : null;
    }
}

然后组合Resolver并全局设置:

// 组合自定义Resolver和带ExcludeNull的StandardResolver,自定义Resolver放在前面
var globalResolver = CompositeResolver.Create(
    NullStringResolver.Instance,
    StandardResolver.ExcludeNull
);

// 全局设置默认Resolver
JsonSerializer.SetDefaultResolver(globalResolver);
方式二:直接传入格式化器列表

如果不想单独写Resolver类,也可以直接将自定义格式化器传入CompositeResolver.Create:

var globalResolver = CompositeResolver.Create(
    new IJsonFormatter[] { new NullStringJsonFormatter() },
    new[] { StandardResolver.ExcludeNull }
);

JsonSerializer.SetDefaultResolver(globalResolver);

3. 验证效果

用你的测试JSON验证:

public class TestModel
{
    public string Info { get; set; }
    public string Name { get; set; }
}

// 测试JSON
var json = @"{""info"":""null"", ""name"":""example""}";
var model = JsonSerializer.Deserialize<TestModel>(json);

// 此时model.Info应为null,model.Name为"example"

关键注意事项

  • 注册顺序:在CompositeResolver中,自定义Resolver/格式化器必须放在StandardResolver前面,因为Utf8Json会按顺序查找第一个匹配的格式化器。
  • 类型覆盖:确保自定义Resolver只针对string类型返回格式化器,避免影响其他类型的序列化逻辑。
  • 全局生效范围:调用JsonSerializer.SetDefaultResolver后,所有未指定特定Resolver的序列化/反序列化操作都会使用这个全局配置,无需再给每个属性加注解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 01:17:44