【问题标题】:Serialisation of custom type inheriting Dictionary using System.Text.Json does not serialise other properties使用 System.Text.Json 对继承 Dictionary 的自定义类型进行序列化不会序列化其他属性
【发布时间】:2021-09-16 02:48:00
【问题描述】:

我有以下类型:

public class Product : Dictionary<string, object>
{
    [JsonInclude]
    public string ProductId { get; set; }

    public Product(string productId) : base()
    {
        ProductId = productId;
    }
}

当使用 System.Text.Json 进行序列化时,它不包括属性(即ProductId)。 添加或删除[JsonInclude] 似乎没有任何效果。

测试用例:

[Fact]
public void SimpleTest()
{
    var p = new Product("ABC123");
    p["foo"] = "bar";
    var json = JsonSerializer.Serialize(p);
    Assert.Contains("productId", json, StringComparison.OrdinalIgnoreCase);
}

并收到输出:

{"foo":"bar"}

在序列化过程中,如何让它在我的类型中包含我的自定义属性? (注意:不要关心反序列化)。

【问题讨论】:

  • 你希望这个输出如何?
  • 是的,这是正确的。 System.Text.Json 仅序列化字典键和值而不是 c# 属性,因为 1)可能有一个与属性同名的键,以及 2)您可能不想要像 Count 这样的“标准”属性和IsReadOnly 被序列化。我在 MSFT 文档中找不到任何说明这一点的地方,但是 Newtonsoft 是 documented to behave this wayDataContractJsonSerializerJavaScriptSerializer 也是如此。 System.Text.Json 似乎有先例。
  • 您可能会考虑一个不同的数据模型,其中Product 不继承自Dictionary,而是具有[System.Text.Json.Serialization.JsonExtensionData] public Dictionary&lt;string, object&gt; Properties { get; set; } 属性。 [JsonExtensionData] 属性导致字典属性在序列化时作为父对象的一部分包含在内。
  • 除此之外,您还需要编写自定义JsonConverter。 (根据docs for JsonInclude 当应用于属性时,表示非公共的getter和setter可以用于序列化和反序列化。所以这里不相关,因为ProductId已经有公共getter 和 setter。)
  • @dbc 实际上 JsonExtensionData 属性可能会起作用,我会检查一下,谢谢。提交它作为答案,我会接受

标签: c# json serialization .net-5 system.text.json


【解决方案1】:

System.Text.Json 不序列化字典属性。 我在 MSFT 文档中找不到任何说明这一点的地方,但 System.Text.Json 仅序列化字典键和值。这可以从DictionaryOfTKeyTValueConverter&lt;TCollection, TKey, TValue&gt; 的参考源确认,这是用于您的类型的转换器。

这可能是因为:

  1. 可能存在与属性同名的键。
  2. 应用开发者几乎肯定不希望像CountIsReadOnly 这样的“标准”字典属性被序列化。
  3. 早期序列化程序的行为方式相同,System.Text.Json 遵循先例。 Newtonsoft 是documented to only serialize dictionary keys and valuesDataContractJsonSerializer(与UseSimpleDictionaryFormat = true)和JavaScriptSerializer 也可以。

作为替代方案,您可以考虑使用不同的数据模型,其中Product 不继承自Dictionary,而是具有[JsonExtensionData] public Dictionary&lt;string, object&gt; Properties { get; set; } 属性:

public class Product 
{
    public string ProductId { get; set; }

    [System.Text.Json.Serialization.JsonExtensionData]
    public Dictionary<string, object> Properties { get; set; } = new ();

    public Product(string productId) : base()
    {
        ProductId = productId;
    }
}

[JsonExtensionData] 属性使字典属性在序列化和反序列化时作为父对象的一部分包含在内。

注意事项:

  • 如果建议的替代方案不可接受,您将需要编写自定义 JsonConverter 来手动序列化您的 Product 字典的 .NET 属性和键和值。

  • [JsonInclude] 添加到ProductId 不会强制它被序列化。根据docs for JsonInclude当应用于属性时,[it] 表示非公共的 getter 和 setter 可用于序列化和反序列化。 所以这里与ProductId 无关已经有公共的 getter 和 setter。

  • Newtonsoft 也有一个JsonExtensionData 属性(它做同样的事情)。如果您同时使用两个序列化程序,请注意使用正确的属性。

  • 在您的问题中,您声明 不关心反序列化,但反序列化确实可以在 JsonExtensionData 上正常工作。

演示小提琴here.

【讨论】:

    猜你喜欢
    • 2021-06-23
    • 1970-01-01
    • 2011-07-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多