如何用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
相关产品推荐
相关产品推荐

