获取.NET Core JsonSerializer 序列化私有成员
Get .NET Core JsonSerializer to serialize private members
我有一个 class 和一个私人 List<T>
属性,我想 serialize/deserialize 使用 JsonSerializer
。 .NET Core 似乎不支持使用 JsonPropertyAttribute
。那么我怎样才能让我的私人列表 属性 序列化呢?
我正在为此使用 System.Text.Json。
好像System.Text.Json不支持私有属性序列化。
但正如微软的文档所说,您可以使用自定义转换器来完成。
序列化代码段;
public class Category
{
public Category(List<string> names)
{
this.Names1 = names;
}
private List<string> Names1 { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
}
public class CategoryJsonConverter : JsonConverter<Category>
{
public override Category Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var name = reader.GetString();
var source = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(name);
var category = new Category(null);
var categoryType = category.GetType();
var categoryProps = categoryType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var s in source.Keys)
{
var categoryProp = categoryProps.FirstOrDefault(x => x.Name == s);
if (categoryProp != null)
{
var value = JsonSerializer.Deserialize(source[s].GetRawText(), categoryProp.PropertyType);
categoryType.InvokeMember(categoryProp.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance,
null,
category,
new object[] { value });
}
}
return category;
}
public override void Write(Utf8JsonWriter writer,
Category value,
JsonSerializerOptions options)
{
var props = value.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.ToDictionary(x => x.Name, x => x.GetValue(value));
var ser = JsonSerializer.Serialize(props);
writer.WriteStringValue(ser);
}
}
static void Main(string[] args)
{
Category category = new Category(new List<string>() { "1" });
category.Name2 = "2";
category.Name3 = "3";
var opt = new JsonSerializerOptions
{
Converters = { new CategoryJsonConverter() },
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var json = JsonSerializer.Serialize(category, opt);
var obj = JsonSerializer.Deserialize<Category>(json, opt);
Console.WriteLine(json);
Console.ReadKey();
}
结果;
"{\"Names1\":[\"1\"],\"Name2\":\"2\",\"Name3\":\"3\"}"
System.Text.Json 根据 micosoft documentation
支持从 .NET 5 开始的私有 属性 序列化
System.Text.Json 通过 [JsonInclude] 属性支持私有和内部 属性 setter 和 getter。Find more details here
虽然不能直接序列化私有字段,但可以间接序列化。
您需要为字段提供 public 属性 和构造函数,如下例所示:
class MyNumbers
{
// This private field will not be serialized
private List<int> _numbers;
// This public property will be serialized
public IEnumerable<int> Numbers => _numbers;
// The serialized property will be recovered with this dedicated constructor
// upon deserialization. Type and name must be the same as the public property.
public MyNumbers(IEnumerable<int> Numbers = null)
{
_numbers = Numbers as List<int> ?? Numbers?.ToList() ?? new();
}
}
以下代码演示了它是如何工作的:
string json;
// Serialization
{
MyNumbers myNumbers = new(new List<int> { 10, 20, 30});
json = JsonSerializer.Serialize(myNumbers);
Console.WriteLine(json);
}
// Deserialization
{
var myNumbers2 = JsonSerializer.Deserialize<MyNumbers>(json);
foreach (var number in myNumbers2.Numbers)
Console.Write(number + " ");
}
输出:
{"Numbers":[10,20,30]}
10 20 30
如果您想阻止人们访问您的私人数据,您可以将名称更改为明确禁止的名称,例如 __private_numbers
。
class MyNumbers2
{
private List<int> _numbers;
public IEnumerable<int> __private_numbers => _numbers;
public MyNumbers2(IEnumerable<int> __private_numbers = null)
{
_numbers = __private_numbers as List<int> ?? __private_numbers?.ToList() ?? new();
}
}
如果外部编码人员愚蠢到可以访问该私有数据,就好像它是 class 的正常编程接口的一部分一样,那么他真可耻。您完全有权更改该“私有界面”而不会感到内疚。而且他也不能用 IEnumerable
.
弄乱你的内部列表
在大多数情况下,这应该足够了。
我有一个 class 和一个私人 List<T>
属性,我想 serialize/deserialize 使用 JsonSerializer
。 .NET Core 似乎不支持使用 JsonPropertyAttribute
。那么我怎样才能让我的私人列表 属性 序列化呢?
我正在为此使用 System.Text.Json。
好像System.Text.Json不支持私有属性序列化。
但正如微软的文档所说,您可以使用自定义转换器来完成。
序列化代码段;
public class Category
{
public Category(List<string> names)
{
this.Names1 = names;
}
private List<string> Names1 { get; set; }
public string Name2 { get; set; }
public string Name3 { get; set; }
}
public class CategoryJsonConverter : JsonConverter<Category>
{
public override Category Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var name = reader.GetString();
var source = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(name);
var category = new Category(null);
var categoryType = category.GetType();
var categoryProps = categoryType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var s in source.Keys)
{
var categoryProp = categoryProps.FirstOrDefault(x => x.Name == s);
if (categoryProp != null)
{
var value = JsonSerializer.Deserialize(source[s].GetRawText(), categoryProp.PropertyType);
categoryType.InvokeMember(categoryProp.Name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance,
null,
category,
new object[] { value });
}
}
return category;
}
public override void Write(Utf8JsonWriter writer,
Category value,
JsonSerializerOptions options)
{
var props = value.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.ToDictionary(x => x.Name, x => x.GetValue(value));
var ser = JsonSerializer.Serialize(props);
writer.WriteStringValue(ser);
}
}
static void Main(string[] args)
{
Category category = new Category(new List<string>() { "1" });
category.Name2 = "2";
category.Name3 = "3";
var opt = new JsonSerializerOptions
{
Converters = { new CategoryJsonConverter() },
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var json = JsonSerializer.Serialize(category, opt);
var obj = JsonSerializer.Deserialize<Category>(json, opt);
Console.WriteLine(json);
Console.ReadKey();
}
结果;
"{\"Names1\":[\"1\"],\"Name2\":\"2\",\"Name3\":\"3\"}"
System.Text.Json 根据 micosoft documentation
支持从 .NET 5 开始的私有 属性 序列化System.Text.Json 通过 [JsonInclude] 属性支持私有和内部 属性 setter 和 getter。Find more details here
虽然不能直接序列化私有字段,但可以间接序列化。
您需要为字段提供 public 属性 和构造函数,如下例所示:
class MyNumbers
{
// This private field will not be serialized
private List<int> _numbers;
// This public property will be serialized
public IEnumerable<int> Numbers => _numbers;
// The serialized property will be recovered with this dedicated constructor
// upon deserialization. Type and name must be the same as the public property.
public MyNumbers(IEnumerable<int> Numbers = null)
{
_numbers = Numbers as List<int> ?? Numbers?.ToList() ?? new();
}
}
以下代码演示了它是如何工作的:
string json;
// Serialization
{
MyNumbers myNumbers = new(new List<int> { 10, 20, 30});
json = JsonSerializer.Serialize(myNumbers);
Console.WriteLine(json);
}
// Deserialization
{
var myNumbers2 = JsonSerializer.Deserialize<MyNumbers>(json);
foreach (var number in myNumbers2.Numbers)
Console.Write(number + " ");
}
输出:
{"Numbers":[10,20,30]}
10 20 30
如果您想阻止人们访问您的私人数据,您可以将名称更改为明确禁止的名称,例如 __private_numbers
。
class MyNumbers2
{
private List<int> _numbers;
public IEnumerable<int> __private_numbers => _numbers;
public MyNumbers2(IEnumerable<int> __private_numbers = null)
{
_numbers = __private_numbers as List<int> ?? __private_numbers?.ToList() ?? new();
}
}
如果外部编码人员愚蠢到可以访问该私有数据,就好像它是 class 的正常编程接口的一部分一样,那么他真可耻。您完全有权更改该“私有界面”而不会感到内疚。而且他也不能用 IEnumerable
.
在大多数情况下,这应该足够了。