通常情况下,我们在编写class时,会将类中的集合属性定义未只读的,这样既可以确保集合数据能够被调用方正常使用,同时也能够防止类中的集合对象被调用方使用新的集合实例替换,保证集合对象的唯一性。通常做法如下所示:
public class ReadonlyPropertyExample{
private readonly List<string> strings;
public CollectionPropertyExample(){
strings = new List<string>();
}
/// <summary>
/// 公开的集合属性设计成只读,并且对外屏蔽了内部实现的细节
/// </summary>
public string[] StringValue{get{return strings.ToArray();}}
/// <summary>
/// 对外提供有限的修改集合的方法
/// </summary>
public void Add(string value){
strings.Add(value);
}
}
上面的代码是比较标准的集合属性实现,即公开了应该公开的数据,同时又对外屏蔽了内部的实现细节,防止调用者任意修改集合数据,破坏封装性。
这样的设计在使用.Net的System.Text.Json进行反序列化的时候会碰到一个比较棘手的问题,就是直接对上面的类进行反序列化,会发现反序列化时数据并没有被正确添加到集合中。
var example = new ReadonlyPropertyExample();
example.Add("Value1");
example.Add("Value2");
var json = JsonSerializer.Serialize(example);
Debug.WriteLine($"example序列化Json:{json}");
var newExample = JsonSerializer.Deserialize<ReadonlyPropertyExample>(json);
Debug.WriteLine($"反序列化Json-----");
Debug.WriteLine($"集合长度:{json.StringValues.Length}");
运行后,输出下面内容
example序列化Json:{"StringValues":["value1","value2"]}
反序列化Json-----
集合长度:0
这并不是我们想要的结果,因为StringValues被设计成只读属性,因此在.NET在进行Json反序列化时,自动忽略了对该属性的处理,所以,想通过给该属性附加转换器的方式间接实现对只读属性进行反序列化的想法也就不可行了,反序列化时根本就不会处理这个属性,也就不会触发附加到这个属性上的转换器了。
那么,有没有办法实现对只读属性的反序列化呢?答案肯定是有的。
可用利用[JsonInclude]特性实现对只读属性的反序列化。将上面的代码修改一下
public class ReadonlyPropertyExample{
private readonly List<string> strings;
public CollectionPropertyExample(){
strings = new List<string>();
}
/// <summary>
/// 公开的集合属性设计成只读,并且对外屏蔽了内部实现的细节
/// </summary>
[JsonInclude]
public string[] StringValue{
get{return strings.ToArray();}
private set{strings.AddRange(value);}
}
/// <summary>
/// 对外提供有限的修改集合的方法
/// </summary>
public void Add(string value){
strings.Add(value);
}
}
将StringValue属性的setter方式的可见性设计为私有,这样不会改变属性的只读特性,所以也就不会破坏最初的设计原则。而JsonInclude特性又可以让Json.Net在反序列化时不在忽略这个只读属性,并可以对私有的setter进行调用,完成对集合数据的还原。
var example = new ReadonlyPropertyExample();
example.Add("Value1");
example.Add("Value2");
var json = JsonSerializer.Serialize(example);
Debug.WriteLine($"example序列化Json:{json}");
var newExample = JsonSerializer.Deserialize<ReadonlyPropertyExample>(json);
Debug.WriteLine($"反序列化Json-----");
Debug.WriteLine($"集合长度:{json.StringValues.Length}");
foreach(var value in newExample.StringValues){
Debug.WriteLine($"数据项:{value}");
}
执行上面的代码后,结果如下:
example序列化Json:{"StringValues":["value1","value2"]}
反序列化Json-----
集合长度:2
数据项:Value1
数据项:Value2
这样的实现方式,个人觉得是相当完美的,应用私有setter的方法可以让反序列化发生更多的变化......

本文介绍了一种在.NET中实现只读集合属性反序列化的方法。通过使用[JsonInclude]特性和私有setter,可以在不破坏封装性的前提下,完成只读属性的序列化与反序列化。

2310

被折叠的 条评论
为什么被折叠?



