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

从Newtonsoft.Json迁移到System.Text.Json仍遇Int32转换错误

问题

从Newtonsoft.Json迁移到System.Text.Json后,原本通过配置NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore可以正常处理null值的id字段,现在出现错误:"The JSON value could not be converted to System.Int32"。参考官方建议将Id字段改为可空类型后,问题仍未解决,请求排查错误原因。

此前Newtonsoft配置

.AddNewtonsoftJson(x => 
{
   x.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; 
})

错误详情

{
   "$id":"1",
   "type":"https://tools.ietf.org/html/rfc9110#section-15.5.1",
   "title":"One or more validation errors occurred.",
   "status":400,
   "errors":{
      "$id":"2",
      "$.id":[
         "The JSON value could not be converted to System.Int32. Path: $.id | LineNumber: 0 | BytePositionInLine:10."
      ]
   },
   "traceId":"00-e707e5d67aedda844c575987294eaab8-50ccf58df2364c2a-00"
}

修改后的实体类代码

[Serializable]
public abstract class Dto<TId> : DomainEntity<TId>
{
    [Key]
    public new TId? Id
    {
        get => (TId?)base.Id;
        set => base.Id = value;
    }
}

[Serializable]
public abstract class Dto : DomainEntity
{
}

[Serializable]
public abstract class DomainEntity<TId> : DomainEntity
{
    [Key]
    public TId? Id { get; set; }
}

[Serializable]
public abstract class DomainEntity
{
    public DateTime CreatedDate { get; set; }
    public DateTime UpdatedDate { get; set; }
}

[Serializable]
public abstract class CreatedBySomeoneDto : Dto<int>
{
    public string CreatedById { get; set; }
    public string UpdatedById { get; set; }
}

[Serializable]
public class VgnItmDto : CreatedBySomeoneDto
{
    [Required]
    [Text(Boost = 2)]
    public string Name { get; set; }

    [Required]
    [Text(Boost = 0.5)]
    public string CompanyName { get; set; }

    [MaxLength(3000)]
    public string Description { get; set; }

    [Required]
    public int IsNotVeganCount { get; set; } = 0;

    [Required]
    public int IsVeganCount { get; set; } = 0;

    [Required]
    public int RatingsCount { get; set; } = 0;

    public double? Rating { get; set; }

    [JsonIgnore]
    private Collection<int> _tags = new Collection<int>();

    [Required]
    [JsonPropertyName("tags")]
    public Collection<int> Tags => new Collection<int>(_tags);

    [JsonIgnore]
    private Collection<int> _secondaryTags = new Collection<int>();

    [JsonPropertyName("secondaryTags")]
    public Collection<int> SecondaryTags => new Collection<int>(_secondaryTags);

    [MaxLength(20)]
    [JsonIgnore]
    private Collection<ImageDto> _images = new Collection<ImageDto>();

    [JsonPropertyName("images")]
    public Collection<ImageDto> Images => new Collection<ImageDto>(_images);

    public int? PricePoint { get; set; }

    [Keyword]
    public string Discriminator { get; set; }

    [MaxLength(900)]
    public string WebPage { get; set; }

    public string Instagram { get; set; }

    public string Facebook { get; set; }

    public void UpdateTags(IEnumerable<int> tags)
    {
        _tags = new Collection<int>(tags.ToList());
    }

    public void UpdateSecondaryTags(IEnumerable<int> secondaryTags)
    {
        _secondaryTags = new Collection<int>(secondaryTags.ToList());
    }

    public void UpdateImages(IEnumerable<ImageDto> images)
    {
        _images = new Collection<ImageDto>(images.ToList());
    }
}

public class MenuItmDto : VgnItmDto
{
}

控制器代码

[Route("[controller]")]
[ApiController]
public class VgnItmsController<TVgnItm, TVgnItmDto, TService> : ControllerBase
    where TVgnItm : VgnItm
    where TVgnItmDto : VgnItmDto
    where TService : IVgnItmsService<TVgnItm, TVgnItmDto>
{
    protected DbSet<TVgnItm> vgnItms;
    protected readonly VpDbContext context;
    protected readonly ExtendedVepoContext extendedContext;
    protected readonly TService service;

    public VgnItmsController(
        TService service)
    {
        this.service = service;
    }

    [HttpPost("create")]
    public async Task<ServiceResponse<TVgnItmDto>> AddVgnItm(TVgnItmDto vgnItm)
    {
        return await service.Add(vgnItm);
    }
}

[Route("menu-items")]
[ApiController]
public class MenuItmsController : VgnItmsController<MenuItm, MenuItmDto, IMenuItmsService>
{
    public MenuItmsController(
        VpDbContext context,
        ExtendedVepoContext extendedContext,
        IMenuItmsService service) :
        base(service)
    {
    }
}

请求JSON体

{
   "id":null,
   "name":"a",
   "companyName":"a",
   "description":null,
   "tags":[
      20
   ],
   "secondaryTags":[
      
   ],
   "images":[
      
   ],
   "webPage":null,
   "instagram":null,
   "facebook":null,
   "rating":null,
   "ratingsCount":0,
   "isNotVeganCount":0,
   "isVeganCount":0,
   "pricePoint":null,
   "createdDate":null,
   "updatedDate":null,
   "createdById":null,
   "updatedById":null,
   "discriminator":"MenuItm"
}
错误原因及解决方案

错误原因

  1. 泛型类型参数未指定可空值类型:CreatedBySomeoneDto继承自Dto<int>,这里的int是非可空值类型。虽然你在Dto<TId>和DomainEntity<TId>中把Id定义为TId?,但当TId是非可空值类型时,System.Text.Json无法正确识别其可空性,仍会尝试将null映射到非可空的int类型。
  2. System.Text.Json默认不忽略null值:Newtonsoft的NullValueHandling.Ignore会跳过null值的反序列化,但System.Text.Json默认会处理所有存在的字段,包括null值。即使Id是可空类型,JSON中存在id:null时,若未配置忽略规则,仍会触发类型转换校验。
  3. 字段隐藏导致的歧义:Dto<TId>中的Id用new关键字隐藏了基类的Id字段,可能导致System.Text.Json反序列化时优先处理基类的非可空Id字段,引发类型转换错误。

解决方案

  1. 修改泛型继承的类型参数为可空值类型
    将CreatedBySomeoneDto的继承改为Dto<int?>,明确指定TId为可空整数:

    [Serializable]
    public abstract class CreatedBySomeoneDto : Dto<int?>
    {
        public string CreatedById { get; set; }
        public string UpdatedById { get; set; }
    }
    
  2. 配置System.Text.Json忽略null值
    在项目配置中添加System.Text.Json的忽略规则,对齐Newtonsoft的行为:

    builder.Services.AddControllers()
        .AddJsonOptions(options =>
        {
            // 序列化和反序列化时都忽略null值
            options.JsonSerializerOptions.IgnoreNullValues = true;
            // 或者仅在值为null时忽略序列化(按需选择)
            // options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
        });
    
  3. 移除new关键字,避免字段隐藏
    删除Dto<TId>中重新定义的Id字段,直接使用基类的Id,消除反序列化的歧义:

    [Serializable]
    public abstract class Dto<TId> : DomainEntity<TId>
    {
        // 直接复用基类的Id字段,无需重新定义
    }
    
  4. 启用nullable引用类型
    在项目的.csproj文件中添加<Nullable>enable</Nullable>,让编译器和System.Text.Json能更准确地识别可空类型:

    <PropertyGroup>
        <Nullable>enable</Nullable>
    </PropertyGroup>
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:44:57