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

.NET 7中DTO字段的条件JSON序列化/反序列化问题

解决方案

方案1:拆分DTO(推荐,符合单一职责)

将用于接收请求和返回响应的DTO分开,避免一个DTO承担两种职责,逻辑更清晰,无序列化冲突问题。

定义专用DTO

// 用于Post/Put请求的DTO(无需Id字段)
public class CreateUpdateUserDTO
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public bool Status { get; set; } = true;
    public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();
}

// 用于Get响应的DTO(包含Id字段)
public class UserResponseDTO
{
    public string Id { get; set; }
    public string Email { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public bool Status { get; set; }
    public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();
}

修改控制器方法

[HttpPost]
[Route("users")]
public async Task<IActionResult> CreateUserAsync(CreateUpdateUserDTO u)
{
    var user = _mapper.Map<User>(u);
    await _usersApplication.CreateAsync(user);
    var response = _mapper.Map<UserResponseDTO>(user);
    return CreatedAtAction(nameof(GetUserByIdAsync), new { userId = response.Id }, response);
}

[HttpGet("user/{userId}")]
public async Task<IActionResult> GetUserByIdAsync(string userId)
{
    var u = await _usersApplication.GetByIdAsync(userId);
    var userResponse = _mapper.Map<UserResponseDTO>(u);
    return Ok(userResponse);
}

此方式下Swagger会自动匹配请求/响应的正确DTO结构,Post请求不会显示Id,Get请求正常返回Id。

方案2:System.Text.Json条件序列化+Swagger过滤器(不拆分DTO场景)

若不想拆分DTO,可通过Json特性控制序列化逻辑,再配合Swagger过滤器隐藏请求中的Id字段。

步骤1:配置DTO的Json特性

使用JsonPropertyAccess.ReadOnly标记Id字段,实现序列化时输出、反序列化时忽略:

public class UserDTO
{
    [JsonPropertyName("id")]
    [JsonProperty(Access = JsonPropertyAccess.ReadOnly)]
    public string Id { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public bool Status { get; set; } = true;
    public List<RoleDTO> Roles { get; set; } = new List<RoleDTO>();
}

步骤2:添加Swagger过滤器隐藏Id字段

创建Swagger操作过滤器,移除Post请求中的Id属性显示:

public class RemoveIdFromCreateUserFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.RequestBody != null 
            && context.ApiDescription.HttpMethod == HttpMethod.Post.ToString() 
            && context.ApiDescription.RelativePath == "users")
        {
            var schema = operation.RequestBody.Content["application/json"].Schema;
            if (schema.Properties.ContainsKey("id"))
            {
                schema.Properties.Remove("id");
            }
        }
    }
}

在Program.cs中注册过滤器:

builder.Services.AddSwaggerGen(c =>
{
    c.OperationFilter<RemoveIdFromCreateUserFilter>();
});

设置完成后:Post请求会忽略传入的Id,Get请求正常返回Id,Swagger的Post示例不再显示Id字段。

方案3:自定义JsonConverter(灵活控制场景)

编写自定义转换器实现更精细的序列化/反序列化逻辑:

public class UserDTOConverter : JsonConverter<UserDTO>
{
    public override UserDTO Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // 反序列化时手动解析,跳过Id字段
        var jsonElement = JsonElement.ParseValue(ref reader);
        var dto = new UserDTO
        {
            Email = jsonElement.GetProperty("Email").GetString(),
            Password = jsonElement.GetProperty("Password").GetString(),
            FirstName = jsonElement.TryGetProperty("FirstName", out var fn) ? fn.GetString() : null,
            LastName = jsonElement.TryGetProperty("LastName", out var ln) ? ln.GetString() : null,
            Status = jsonElement.TryGetProperty("Status", out var status) ? status.GetBoolean() : true,
            Roles = jsonElement.TryGetProperty("Roles", out var roles) 
                ? JsonSerializer.Deserialize<List<RoleDTO>>(roles.GetRawText(), options) 
                : new List<RoleDTO>()
        };
        return dto;
    }

    public override void Write(Utf8JsonWriter writer, UserDTO value, JsonSerializerOptions options)
    {
        // 序列化时正常输出所有字段(含Id)
        JsonSerializer.Serialize(writer, value, options);
    }
}

在DTO上添加转换器特性:

[JsonConverter(typeof(UserDTOConverter))]
public class UserDTO
{
    // 字段定义不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:08:11