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

.NET 6 Web API添加自定义JsonConverter后驼峰命名失效求助

.NET 6 Minimal Web API自定义JSON转换器后CamelCase命名策略失效问题解决

问题描述

在.NET 6的Minimal Web API中添加自定义JSON转换器后,序列化/反序列化属性不再遵循默认的CamelCase命名策略,始终输出PascalCase格式。目前已通过显式调用PropertyNamingPolicy.ConvertName方法临时解决,但希望找到让控制器自动遵循命名策略的方案。

已尝试但无效的操作:

  • 服务配置阶段显式指定JsonNamingPolicy.CamelCase
  • 在自定义FooModelJsonConverter内创建新的JsonSerializerOptions实例
  • 控制器中返回显式ActionResult<T>类型

相关代码

Program.cs

using System.Text.Json;
using Microsoft.AspNetCore.Http.Json;
using MinimalWebApi.Converters;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddMvc();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.Configure<JsonOptions>(
    options => {
        options.SerializerOptions.Converters.Add(new FooModelJsonConverter());
        // 显式设置也无效果
        //options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    }
);

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

FooModelJsonConverter.cs

using System.Text.Json;
using System.Text.Json.Serialization;
using MinimalWebApi.Model;

namespace MinimalWebApi.Converters;

public class FooModelJsonConverter: JsonConverter<FooModel>
{
    public override FooModel Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options
    )
    {
        return JsonSerializer.Deserialize<FooModel>(ref reader, options)
            ?? throw new JsonException("FooModel反序列化为null!");
    }

    public override void Write(
        Utf8JsonWriter writer,
        FooModel fooModel,
        JsonSerializerOptions options
    ) {
        writer.WriteStartObject();

        var majorVersion = fooModel.MajorVersion;
        var minorVersion = fooModel.MinorVersion;

        if (majorVersion == null || minorVersion == null)
            throw new JsonException($"缺少Major/Minor属性,当前值:MajorVersion: {majorVersion}, MinorVersion: {minorVersion}");

        switch (majorVersion)
        {
            case 1:
                writer.WritePropertyName(TryParseCamelCase(nameof(fooModel.Id), options, true));
                JsonSerializer.Serialize(writer, fooModel.Id, options);
                switch (minorVersion)
                {
                    case 3:
                        writer.WritePropertyName(TryParseCamelCase(nameof(fooModel.AnotherId), options, true));
                        JsonSerializer.Serialize(writer, fooModel.AnotherId, options);
                        break;
                    case 0: case 1: case 2:
                        break;
                    default:
                        throw new JsonException($"未知Minor版本:{minorVersion}");
                }
                writer.WritePropertyName(TryParseCamelCase(nameof(fooModel.Name), options, true));
                JsonSerializer.Serialize(writer, fooModel.Name, options);
                writer.WritePropertyName(TryParseCamelCase(nameof(fooModel.Status), options, true));
                JsonSerializer.Serialize(writer, fooModel.Status, options);
                break;
            default:
                throw new JsonException($"未知Major版本:{majorVersion}");
        }

        writer.WriteEndObject();
    }

    public static string TryParseCamelCase(string propertyName, JsonSerializerOptions options, bool disabled = false)
    {
        var namingPolicy = options.PropertyNamingPolicy;
        return !disabled && namingPolicy != null ? namingPolicy.ConvertName(propertyName) : propertyName;
    }
}

FooModel.cs

using System.Text.Json.Serialization;
using MinimalWebApi.Converters;

namespace MinimalWebApi.Model
{
    [JsonConverter(typeof(FooModelJsonConverter))]
    public class FooModel
    {
        public enum State { STARTED, RUNNING, COMPLETED, FAILED };

        public Guid Id { get; set; }
        public string? AnotherId { get; set; }
        public string? Name { get; set; }

        [JsonConverter(typeof(JsonStringEnumConverter))]
        public State Status { get; set; }

        [JsonIgnore]
        public int? MajorVersion { get; set; } = 1;
        
        [JsonIgnore]
        public int? MinorVersion { get; set; } = 0;
    }
}

解决方案

1. 修复转换器中的命名策略开关

当前代码中调用TryParseCamelCase时传入了disabled=true,直接跳过了命名策略转换,这是PascalCase输出的核心原因。修改Write方法中所有WritePropertyName的调用,将最后一个参数改为false:

// 原代码
writer.WritePropertyName(TryParseCamelCase(nameof(fooModel.Id), options, true));
// 修改后
writer.WritePropertyName(TryParseCamelCase(nameof(fooModel.Id), options, false));

修改后,转换器会自动使用配置的CamelCase命名策略生成属性名。

2. 配置正确的JSON选项类

你使用了MapControllers(),属于MVC控制器体系,而Microsoft.AspNetCore.Http.Json.JsonOptions是为纯Minimal API的端点路由设计的,两者不兼容。需要替换命名空间并配置MVC的JsonOptions:

  1. 替换Program.cs中的using语句:

    // 移除
    // using Microsoft.AspNetCore.Http.Json;
    // 添加
    using Microsoft.AspNetCore.Mvc;
    
  2. 修改配置代码:

    builder.Services.Configure<JsonOptions>(
        options => {
            options.JsonSerializerOptions.Converters.Add(new FooModelJsonConverter());
            options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        }
    );
    

这就是StackOverflow帖子建议替换命名空间的原因——选错配置类会导致所有JSON设置失效。

3. 优化转换器实现(可选)

如果不需要手动拼接JSON,可以利用匿名类结合版本逻辑,让JsonSerializer自动处理命名策略,简化代码:

public override void Write(Utf8JsonWriter writer, FooModel fooModel, JsonSerializerOptions options)
{
    var serializedObj = fooModel.MajorVersion switch
    {
        1 => fooModel.MinorVersion switch
        {
            0 or 1 or 2 => new { fooModel.Id, fooModel.Name, fooModel.Status },
            3 => new { fooModel.Id, fooModel.AnotherId, fooModel.Name, fooModel.Status },
            _ => throw new JsonException($"未知Minor版本:{fooModel.MinorVersion}")
        },
        _ => throw new JsonException($"未知Major版本:{fooModel.MajorVersion}")
    };
    JsonSerializer.Serialize(writer, serializedObj, options);
}

这种方式完全依赖框架的序列化逻辑,无需手动处理属性名转换,更不易出错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 11:32:06