c# - JSON.NET 如何设置只获取的 IEnumerable财产?
问题描述
在以下示例中,foo
和deserializedFoo
变量都具有完全填充的Values
属性。一个私有的、无操作的构造函数被赋予该[JsonConstructor]
属性以避免来自公共构造函数的构造函数值映射。
尽管如此,如果 JSON.NET 在构造期间将其支持字段分配给空列表,则似乎能够设置 get-only 属性的值。
我无法在 JSON.NET 文档中找到任何提及此行为的内容。谁能解释这种行为?
void Main()
{
var foo = new Foo(new [] { "One", "Two", "Three" });
var json = JsonConvert.SerializeObject(foo);
var deserializedFoo = JsonConvert.DeserializeObject<Foo>(json);
}
class Foo
{
private readonly List<string> _values;
public Foo(IEnumerable<string> values)
{
_values = values.ToList();
}
[JsonConstructor]
private Foo()
{
_values = new List<string>();
}
public IEnumerable<string> Values => _values;
}
解决方案
我不确定这是在哪里记录的(如果有的话),但是在对象的反序列化过程中,如果已经分配了引用类型的成员,Json.NET 将重新使用现有的成员实例而不是分配新的成员实例。然后它将使用成员值的实际具体类型(而不是声明的类型)将 JSON 填充到成员中。在您的情况下,返回的对象的实际具体类型Values
是List<string>
- Json.NET 能够填充的类型。
如果我更改Values
为实际返回一个只读枚举,如下所示:
public IEnumerable<string> Values => _values.AsReadOnly();
然后Values
不填充 - 演示小提琴 #1这里。
另一方面,如果我将声明的类型更改Values
为object
:
public object Values => _values;
It is populated. Demo fiddle #2 here.
And as a final demonstration, if I modify your Foo
model to have some additional pre-allocated, read-only property Bar
that is only declared as object
like so:
class Foo
{
private readonly List<string> _values;
private readonly Bar _bar = new Bar();
public Foo(IEnumerable<string> values)
{
_values = values.ToList();
}
[JsonConstructor]
private Foo()
{
_values = new List<string>();
}
public IEnumerable<string> Values { get { return _values; } }
public object Bar { get { return _bar; } }
}
public class Bar
{
public string Property1 { get; set; }
}
Then the actual object referred to by the property will be successfully populated:
var json = @"{""Values"":[""One"",""Two"",""Three""],""Bar"":{""Property1"":""Test""}}";
var deserializedFoo = JsonConvert.DeserializeObject<Foo>(json);
Assert.IsTrue(deserializedFoo.Values.Count() == 3 && ((Bar)deserializedFoo.Bar).Property1 == "Test"); // No Assert
Demo fiddle #3 here.
So what are your options, if you don't want this behavior?
Firstly, you could wrap your collection in a read-only wrapper as shown above. This is not a bad idea in general, because it prevents consumers of your Foo
type from simply casting Values
to a List<T>
and modifying it.
Secondly, you could deserialize using ObjectCreationHandling.Replace
. When this setting is enabled, Json.NET will always allocate new values for pre-allocated read/write properties -- and skip over pre-allocated read-only properties. Demo fiddle #4 here. Note this prevents population of read-only member referring to any .Net reference type, not just collections.
Thirdly, you could adopt the approach from this answer to Serialize Property, but Do Not Deserialize Property in Json.Net by Pavlo Lissov and create a custom contract resolver that sets JsonProperty.ShouldDeserialize
to a predicate returning false for properties that should not get deserialized.
推荐阅读
- regex - Perl 正则表达式 - 如何使用 localFile(.txt) 进行匹配
- unix - 几秒钟后,Logstash 服务单独停止
- angular - 如何修复“错误:StaticInjectorError(AppModule)[NgbDropdown -> ChangeDetectorRef]”
- google-sheets - OAuth 客户端已删除
- angular - 理解复杂的类型脚本类型声明
- lambda - 使用 Cognito 在 lambda 中部署时,如何在没有 cookie 和会话的情况下保持 Django REST API 无状态
- c++ - 预处理器定义在发布模式下工作,而不是在调试模式下工作
- python - 我可以在 buildroot 中安装 wheel 包吗?
- bash - bash 仅针对用户输入的第一个字符读取超时
- python - 用python的正则表达式匹配多组字符