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

如何用Newtonsoft JSON反序列化列表时自动生成自增ID?

当然可以用JsonConverter完美解决这个需求!这既能保持Id属性的封装性(私有setter不对外暴露),又能在反序列化过程中自动完成自增赋值,完全符合编程规范。下面我给你两种可行的实现方案,你可以根据自己的场景选择:

方案一:使用内部构造函数(推荐,更优雅)

这种方式不需要依赖反射,而是通过给Person类添加一个内部构造函数,让转换器可以安全地初始化Id,同时不破坏封装性。

第一步:修改Person类(仅添加内部构造函数)

public class Person 
{ 
    public int Id { get; private set; } 
    public string FirstName; 
    public string LastName; 
    public int Gender; 

    // 无参构造函数,供Json.NET反序列化基础属性使用
    public Person() { }

    // 内部构造函数,仅允许同程序集的代码初始化Id
    internal Person(int id)
    {
        Id = id;
    }
}

如果你的转换器和Person类不在同一个程序集,可以在Person所在的项目中添加InternalsVisibleTo特性,允许转换器所在的程序集访问内部成员:

[assembly: InternalsVisibleTo("YourConverterAssemblyName")]

第二步:实现自定义JsonConverter

我们针对List<Person>编写转换器,在遍历JSON数组时逐个创建Person实例并设置自增Id:

public class PersonListConverter : JsonConverter<List<Person>>
{
    public override List<Person> ReadJson(JsonReader reader, Type objectType, List<Person> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var personList = new List<Person>();
        var jArray = JArray.Load(reader);

        // 遍历JSON数组,索引从0开始,Id自增从1开始(可根据需求调整为i)
        for (int i = 0; i < jArray.Count; i++)
        {
            // 创建带Id的Person实例
            var person = new Person(i + 1);
            // 填充其他属性(FirstName、LastName、Gender)
            serializer.Populate(jArray[i].CreateReader(), person);
            
            personList.Add(person);
        }

        return personList;
    }

    public override void WriteJson(JsonWriter writer, List<Person> value, JsonSerializer serializer)
    {
        // 序列化时直接使用默认逻辑即可
        serializer.Serialize(writer, value);
    }

    public override bool CanWrite => true; // 允许序列化(如果不需要可以设为false)
}

第三步:使用转换器反序列化

string json = @"[{""first_name"":""Foo"",""last_name"":""Bar"",""gender"":0},{""first_name"":""FooFoo"",""last_name"":""BarBar"",""gender"":1}]";

var settings = new JsonSerializerSettings();
settings.Converters.Add(new PersonListConverter());

var personList = JsonConvert.DeserializeObject<List<Person>>(json, settings);
// 此时personList中第一个Person的Id为1,第二个为2,以此类推

方案二:使用反射(无需修改Person类)

如果不想修改Person类的结构,可以通过反射来访问Id的私有setter。这种方式更灵活,但性能略低于方案一。

实现自定义JsonConverter

public class PersonListConverter : JsonConverter<List<Person>>
{
    public override List<Person> ReadJson(JsonReader reader, Type objectType, List<Person> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var personList = new List<Person>();
        var jArray = JArray.Load(reader);
        // 获取Id属性的信息,包括私有setter
        var idProperty = typeof(Person).GetProperty(nameof(Person.Id), BindingFlags.Public | BindingFlags.Instance);

        for (int i = 0; i < jArray.Count; i++)
        {
            // 先反序列化得到Person实例
            var person = jArray[i].ToObject<Person>(serializer);
            // 通过反射设置Id值
            idProperty.SetValue(person, i + 1);
            
            personList.Add(person);
        }

        return personList;
    }

    public override void WriteJson(JsonWriter writer, List<Person> value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

使用方式和方案一完全一致,直接在反序列化时添加转换器即可。

为什么这两种方案符合规范?

  • Id的setter始终保持私有,外部代码无法直接修改,保证了数据的不可变性和封装性。
  • 自增逻辑被封装在转换器中,和业务代码解耦,符合单一职责原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:45:42