获取.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不支持私有属性序列化。

https://docs.microsoft.com/tr-tr/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#internal-and-private-property-setters-and-getters

但正如微软的文档所说,您可以使用自定义转换器来完成。

https://www.thinktecture.com/en/asp-net/aspnet-core-3-0-custom-jsonconverter-for-the-new-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.

弄乱你的内部列表

在大多数情况下,这应该足够了。