.NET 8中如何将JSON反序列化到私有字段?
通过阅读.NET 8序列化相关公告与文档,原本认为可以将JSON反序列化到私有字段,编写了如下代码:
public class User { [JsonInclude] [JsonPropertyName("name")] private string _name = null!; public int Age { get; } [JsonConstructor] private User(string name, int age) { _name = name; Age = age; } private User(){} } // 反序列化代码 var userStr = @"{""name"": ""juan"", ""age"": 31}"; var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, IncludeFields = true, IgnoreReadOnlyFields = false }; var user = JsonSerializer.Deserialize<User>(userStr, options)!;
运行后抛出异常:
Unhandled exception. System.InvalidOperationException: Each parameter in the deserialization constructor on type 'User' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. Fields are only considered when 'JsonSerializerOptions.IncludeFields' is enabled. The match can be case-insensitive.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ConstructorParameterIncompleteBinding(Type parentType)...
将私有字段改为带getter的公共属性(如public string Name { get; })可以正常运行,但希望使用私有字段。实际场景中存在一个private List<T>,仅对外暴露public IReadOnlyCollection<T>的只读视图,询问是否可以让序列化程序使用私有字段。
问题的核心是带[JsonConstructor]标记的构造函数参数,需要与类的字段/属性名称匹配(或通过特性显式映射),代码中构造函数参数name找不到对应名称的成员(字段是_name),因此抛出异常。以下两种方法可解决问题:
方法1:给构造函数参数添加[JsonPropertyName]特性
通过特性显式将构造函数参数与JSON字段名绑定,同时保留私有字段的[JsonInclude]特性,确保序列化器能识别私有字段:
public class User { [JsonInclude] [JsonPropertyName("name")] private string _name = null!; public int Age { get; } [JsonConstructor] private User([JsonPropertyName("name")] string name, int age) { _name = name; Age = age; } private User(){} }
方法2:修改构造函数参数名与私有字段名一致
将构造函数的name参数改为_name,让参数名直接匹配私有字段名,序列化器会自动完成绑定:
public class User { [JsonInclude] [JsonPropertyName("name")] private string _name = null!; public int Age { get; } [JsonConstructor] private User(string _name, int age) { this._name = _name; Age = age; } private User(){} }
针对私有集合的实际场景
对于私有List<T>字段+只读对外暴露的场景,同样可以用上述思路处理:
public class MyClass<T> { [JsonInclude] [JsonPropertyName("items")] private List<T> _items = new(); public IReadOnlyCollection<T> Items => _items.AsReadOnly(); [JsonConstructor] private MyClass([JsonPropertyName("items")] List<T> items) { _items = items ?? new List<T>(); } private MyClass(){} }
配置后,序列化器会正确将JSON中的items数组反序列化到私有_items字段,对外仅暴露只读视图。
内容的提问来源于stack exchange,提问作者Juan Francisco Porto

