我注意到,默认情况下,JSON.NET只会(取消)序列化对象的公共属性。这很好。但是,当使用[JsonPropertyAttribute]
标记属性时,JSON.NET还将访问私有 getter和setter。这很糟糕。
我想要解决此问题的方法是使用[JsonIgnoreAttribute]
标记私有getter / setter。
例如:
public class JsonObject
{
[JsonProperty(PropertyName = "read_write_property")]
public object ReadOnlyProperty
{
get;
[JsonIgnore] private set;
}
}
不幸的是,这不是有效的C#代码。那么什么代码可以实现相同的结果呢?
我知道的一些想法可行:
这是唯一的两个选择吗?
我为只读属性添加了支持字段。不确定,但我想我在Json.Net中发现了一个错误。即使指定的name属性与JSON字符串匹配,序列化程序也会在仅存在getter时将该属性视为不存在。这尤其令人讨厌,因为我也在使用[JsonExtensionData]
机制。因此,反序列化的值最终会进入我的扩展数据字典。以下是演示此问题的代码:
违规类
using System.ComponentModel;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public class JsonObject
{
private readonly BindingDirection bindingDirection;
public JsonObject()
{
this.bindingDirection = BindingDirection.OneWay;
}
[JsonProperty(PropertyName = "binding_type"), JsonConverter(typeof(StringEnumConverter))]
public BindingDirection BindingDirection
{
get
{
return this.bindingDirection;
}
}
[JsonExtensionData]
public IDictionary<string, object> ExtensionData { get; set; }
}
示范
using Newtonsoft.Json;
class Program
{
static void Main(string[] args)
{
var obj = new JsonObject();
var serialized = JsonConvert.SerializeObject(obj);
var deserialized = JsonConvert.DeserializeObject<JsonObject>(serialized);
Console.WriteLine("*** Extension data ***\n");
foreach (var kvp in deserialized.ExtensionData)
{
Console.WriteLine("{0} == {1}", kvp.Key, kvp.Value);
}
Console.ReadLine();
}
}
输出
***扩展数据***
binding_type == OneWay
答案 0 :(得分:4)
Json.Net在使用[JsonProperty]
属性标记时访问私有成员的事实实际上是设计的。此功能的存在是为了使Json.Net能够用于序列化/反序列化对象的内部状态(例如,保持它),而不需要为该状态公开公共接口。
来自documentation:
<强> JsonPropertyAttribute 强>
JsonPropertyAttribute有许多用途:
默认情况下,JSON属性与.NET属性具有相同的名称。此属性允许自定义名称。
表示当成员序列化设置为选择加入时,应该序列化属性。
包括序列化和反序列化中的非公共属性。
自定义属性值的类型名称,引用,空值和默认值处理。
自定义属性的集合项JsonConverter,类型名称处理和引用处理。
不幸的是,[JsonProperty]
属性的参数显然没有提供退出包括非公开成员的方法,同时仍然允许您使用其他功能,例如更改属性名称。因此,在这种情况下,您将不得不使用解决方法。
您已经提到了几种可能性,我将添加第三种可能性:
[JsonPropertyAttribute]
JsonConverter
其中,第二个是最干净,最容易实现的,同时仍然可以实现您正在寻找的结果:公共属性将使用自定义属性名称进行序列化,而私有后备字段在反序列化期间不会受到影响。
删除属性的第一个选项将无法满足您的需求。虽然它会阻止字段反序列化,但您必须解决在序列化过程中写入JSON的原始属性名称。
编写转换器的第三个选项使您可以完全控制类的序列化和反序列化方式。您可以更改属性名称,省略属性,包括甚至不在类中的其他信息,等等。他们并不难写;但是,如果你真正需要的只是一个简单的支持领域来解决你的问题,那么转换器在这里可能有点过分。也就是说,如果你真的对这个选项感兴趣,我很乐意提供一个简单的例子。请告诉我。
关于您的扩展数据问题 - 您是对的,这看起来像一个错误。当我最初尝试重现问题时,我不能,因为我的JsonObject
版本不包含默认构造函数。我假设你的构造函数有一个参数来接受只读属性的初始值。一旦我删除了参数,它就开始行为不端了。我不确定为什么构造函数会对JsonExtensionData的解释方式产生影响。但是,这确实提出了一个奇怪的解决方法:尝试将以下内容添加到您的类中,看看是否能解决问题。
[JsonConstructor]
private JsonObject(string dummy) : this()
{
}