且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

JSON.Net序列化派生类

更新时间:2023-02-15 15:19:07

这里的问题是您的Group<K, T>是一个也具有属性的集合.由于JSON容器可以是数组(无属性)或对象(具有命名的键/值对),因此具有自定义属性的集合不能自动映射到其中一个而不会丢失数据. Json.NET(以及所有其他序列化程序AFAIK)选择映射项而不是自定义属性.

The issue here is that your Group<K, T> is a collection that also has properties. Since a JSON container can either be an array (with no properties) or an object (with named key/value pairs), a collection with custom properties cannot be mapped automatically to either without data loss. Json.NET (and all other serializers AFAIK) choose to map the items not the custom properties.

您可以通过以下几种方式处理此问题:

You have a couple ways to deal with this:

  1. 编写您自己的自定义 JsonConverter .您可以按照 Json.Net返回空括号的方式使用反射来确定通用参数. .

[JsonObject] 标记您的Group<K, T> .

第二个选项似乎最简单,看起来像:

The second option seems simplest, and would look like:

[JsonObject(MemberSerialization = MemberSerialization.OptIn)] // OptIn to omit the properties of the base class,  e.g. Count
class Group<K, T> : ObservableCollection<T>
{
    [JsonProperty("Header")]
    public K Key { get; set; }

    [JsonProperty("Items")]
    IEnumerable<T> Values
    {
        get
        {
            foreach (var item in this)
                yield return item;
        }
        set
        {
            if (value != null)
                foreach (var item in value)
                    Add(item);
        }
    }

    public Group(K Header, IEnumerable<T> Items) // Since there is no default constructor, argument names should match JSON property names.
        : base(Items)
    {
        Key = Header;
    }
}

偶然地,您还有另一个问题-您的Ingredient类没有默认构造函数,并且如果line参数为null,则其单个参数化将抛出NullReferenceException.在没有默认构造函数的情况下,Json.NET将调用单个参数化的构造函数,并按名称将JSON对象值映射到构造函数参数.因此,反序列化将引发异常.

Incidentally, you have another problem -- your Ingredient class does not have a default constructor, and its single parameterized throws a NullReferenceException if the line argument is null. In the absence of a default constructor Json.NET will call the single parameterized constructor, mapping JSON object values to constructor arguments by name. Thus, deserialization throws an exception.

您有几种方法可以解决此问题:

You have a few ways to deal with this:

  1. 添加公共默认构造函数.

  1. Add a public default constructor.

添加一个 private 默认构造函数,并使用

Add a private default constructor and mark it with [JsonConstructor]:

[JsonConstructor]
Ingredient() { }

  • 添加 private 默认构造函数,并使用

  • Add a private default constructor and deserialize with ConstructorHandling.AllowNonPublicDefaultConstructor:

    var settings = new JsonSerializerSettings { ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor };
    var recipe = JsonConvert.DeserializeObject<Recipe>(json, settings);
    

  • 在构造函数中添加一个if (line != null)检查. (不建议这样做.相反,您的构造函数应显式抛出ArgumentNullException.)

  • Add an if (line != null) check in the constructor. (Not really recommended. Instead your constructor should explicitly throw an ArgumentNullException.)

    完成此操作后,您将看到如下所示的gt JSON

    Having done this, you will gt JSON that looks like:

    {
      "IngredientsWithHeaders": [
        {
          "Header": "BlankHeader",
          "Items": [
            {
              "Quantity": "3",
              "Modifier": null,
              "Unit": "tbsp",
              "IngredientName": "butter",
              "Preparation": null
            }
          ]
        }
      ],
    }
    

    您建议的JSON与

    {
    "IngredientsWithHeaders": [
        {
            "Group": {
                "Header": "BlankHeader",
    

    这个额外的"Group"对象是不必要的.

    This extra "Group" object is unnecessary.