JsonSerializer 在反序列化期间不使用内部构造函数
Posted
技术标签:
【中文标题】JsonSerializer 在反序列化期间不使用内部构造函数【英文标题】:JsonSerializer not using internal constructor during deserialization 【发布时间】:2021-12-08 14:13:19 【问题描述】:我正在编写一个库来访问用于管理后端系统中的数据的 Web API。 该库还将为类提供适当管理数据的逻辑。 由于这个原因,大多数类不能有公共的属性设置器或接受所有属性的公共构造函数。相反,我正在尝试使用具有内部访问修饰符的构造函数来设置所有属性。
当尝试使用 System.Text.Json 反序列化 JSON 字符串时,它会忽略内部构造函数 - 它会使用其他构造函数或抛出异常。我也尝试用 [JsonConstructor]
注释内部构造函数,但它什么也没做。
具有内部构造函数的示例类:
public class Entry
public int Id get;
public string Name get; set;
public string Value get; set;
[JsonConstructor]
internal Entry(int id, string name, string value)
this.Id = id;
this.Name = name;
this.Value = value;
假设用户可以使用库从后端获取Entry
,修改其名称和值,并更新它。用户不能更改Id
属性或创建新的Entry
。
反序列化示例:
var json = "\"id\": 1337, \"name\": \"TestEntry\", \"value\": \"TestValue\"";
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
var entry = JsonSerializer.Deserialize<Entry>(json, options);
调用JsonSerializer.Deserialize
将抛出System.NotSupportedException
- 不支持对没有无参数构造函数、单一参数化构造函数或带有“JsonConstructorAttribute”注释的参数化构造函数的类型进行反序列化:
一个可能的解决方案是只公开构造函数并使用[Obsolete("Message", true)]
属性对其进行注释 - 当尝试使用此构造函数时,编译器将抛出错误,使构造函数无法使用。
但我觉得这不是最优雅的解决方案,它还滥用了[Obsolete]
属性。
有没有其他方法可以解决这个问题?
【问题讨论】:
你从哪里得到 json 字符串的开头?如果类是内部的,那么它并不意味着暴露在外部。您可能需要一个 DTO 来往来映射 System.Text.Json 不支持内部构造函数。请检查文档。 docs.microsoft.com/en-us/dotnet/standard/serialization/… 请记住,反射总是可以用来克服您在公共接口中设置的限制,因此有足够决心的人可以更改对象的 ID -- 永远不要依赖用于执行安全边界的接口。鉴于此,通常最好让人们访问公共构造函数,这些构造函数一致地初始化对象,而不用担心谁在调用构造函数。验证数据应该在稍后阶段完成(并且永远不要跳过)。当然,在适当的情况下,属性仍然应该是不可变的,以防止可预防的错误。 @arynaq JSON 来自 Web API(使用 HttpClient 和 GetFromJsonAsync 扩展方法)。类是外部的。似乎使用 DTO 是可行的方法,尽管我希望使用更“直接”的方法。 @JeroenMostert 当然,反射总是可以绕过东西,正如你提到的,正确的检查是在后端完成的。我正在尝试做的是限制使用库时的错误数量 - 与其他开发人员沟通哪些属性是不可变的,哪些可以更新。 【参考方案1】:在这种情况下,实现此目的的最佳方法是使用单独的模型进行序列化。然后在域模型上使用内部构造函数。
public class EntryDto
public int Id get;
public string Name get;
public string Value get;
[JsonConstructor]
public EntryDto(int id, string name, string value)
this.Id = id;
this.Name = name;
this.Value = value;
public Entry ToDomainModel()
return new Entry(this.Id, this.Name, this.Value);
public class Entry
public int Id get;
public string Name get;
public string Value get;
internal Entry(int id, string name, string value)
this.Id = id;
this.Name = name;
this.Value = value;
```
【讨论】:
以上是关于JsonSerializer 在反序列化期间不使用内部构造函数的主要内容,如果未能解决你的问题,请参考以下文章
ProtoBuf 在反序列化期间损坏字节数组(添加了额外的 0)