JSON.Net序列化派生类

时间:2015-08-18 01:05:37

标签: c# json serialization json.net

要使用我正在开发的配方Web服务,我有以下类来保存和序列化配方数据:

class Recipe {

    public string RecipeId { get; private set; }

    public string UserId { get; set; }
    public string Title { get; set; }

    public IList<string> IngredientsList { get; set; }

    public List<Group<string, Ingredient>> IngredientsWithHeaders { get; set; }
    public List<Group<string, string>> InstructionsWithHeaders { get; set; }

    public List<string> Notes { get; set; }
    public ISet<string> Tags { get; set; }
    public int Yield { get; set; }

    public Recipe(string recipeId)
    {
        RecipeId = recipeId;
        IngredientsWithHeaders = new List<Group<string,Ingredient>>();
        InstructionsWithHeaders = new List<Group<string, string>>();
        IngredientsList = new List<string>();
    }

    public byte[] Image { get; set; }
}

class Ingredient
{
    public string Quantity { get; set; }
    public string Modifier { get; set; }
    public string Unit { get; set; }
    public string IngredientName { get; set; }
    public string Preparation { get; set; }

    public Ingredient(string[] line)
    {
        if (!string.IsNullOrWhiteSpace(line.ElementAt(0)))
        {
            Quantity = line.ElementAt(0);
        }
        if (!string.IsNullOrWhiteSpace(line.ElementAt(1)))
        {
            Unit = line.ElementAt(1);
        }
        if (!string.IsNullOrWhiteSpace(line.ElementAt(2)))
        {
            IngredientName = line.ElementAt(2);
        }
        if(line.Length>3)
        {
            Preparation = line.Last();
        }
    }
}

class Group<K, T> : ObservableCollection<T>
{
    public K Key { get; set; }

    public Group(K key, IEnumerable<T> items) : base(items)
    {
        Key = key;
        Debug.WriteLine(key);
    }
}    

我为List<Group<string, Ingredient>>获取的JSON输出是

{
"IngredientsWithHeaders": [
    [
        {
            "Quantity": "3",
            "Modifier": null,
            "Unit": "tbsp",
            "IngredientName": "butter",
            "Preparation": null
        },
        {
            "Quantity": "1",
            "Modifier": null,
            "Unit": "16 oz. bag",
            "IngredientName": "marshmallows",
            "Preparation": null
        },
        {
            "Quantity": "2/3",
            "Modifier": null,
            "Unit": "cup",
            "IngredientName": "dry cake mix",
            "Preparation": null
        },
        {
            "Quantity": "6",
            "Modifier": null,
            "Unit": "cups",
            "IngredientName": "crispy rice cereal",
            "Preparation": null
        },
        {
            "Quantity": "1",
            "Modifier": null,
            "Unit": "container",
            "IngredientName": "sprinkles",
            "Preparation": "optional"
        }
        ]
    ]
}

我希望得到的更多是

{
"IngredientsWithHeaders": [
    {
        "Group": {
            "Header": "BlankHeader",
            "Items": [
                {
                    "Quantity": "3",
                    "Modifier": null,
                    "Unit": "tbsp",
                    "IngredientName": "butter",
                    "Preparation": null
                },
                {
                    "Quantity": "1",
                    "Modifier": null,
                    "Unit": "16 oz. bag",
                    "IngredientName": "marshmallows",
                    "Preparation": null
                },
                {
                    "Quantity": "2/3",
                    "Modifier": null,
                    "Unit": "cup",
                    "IngredientName": "dry cake mix",
                    "Preparation": null
                },
                {
                    "Quantity": "6",
                    "Modifier": null,
                    "Unit": "cups",
                    "IngredientName": "crispy rice cereal",
                    "Preparation": null
                },
                {
                    "Quantity": "1",
                    "Modifier": null,
                    "Unit": "container",
                    "IngredientName": "sprinkles",
                    "Preparation": "optional"
                }
                ]
            }
        }
    ]
}

我是否需要编写自定义序列化程序?如果是这样,我如何在不知道它是

的情况下将对象转换为参数化组
Group<string, Ingredient> 

Group<string, string>

1 个答案:

答案 0 :(得分:1)

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

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

  1. 编写您自己的自定义JsonConverter。您可以使用Json.Net returns Empty Brackets

  2. 行中的反射来确定通用参数
  3. [JsonObject]标记Group<K, T>

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

    [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类没有默认构造函数,如果NullReferenceException参数为null,则其单个参数化会抛出line。在没有默认构造函数的情况下,Json.NET将调用单个参数化构造函数,按名称将JSON对象值映射到构造函数参数。因此,反序列化会引发异常。

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

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

    2. 添加私有默认构造函数,并使用[JsonConstructor]标记:

      [JsonConstructor]
      Ingredient() { }
      
    3. 添加私有默认构造函数,并使用ConstructorHandling.AllowNonPublicDefaultConstructor反序列化:

      var settings = new JsonSerializerSettings { ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor };
      var recipe = JsonConvert.DeserializeObject<Recipe>(json, settings);
      
    4. 在构造函数中添加if (line != null)项检查。 (不是真的推荐。相反,你的构造函数应该显式抛出ArgumentNullException。)

    5. 完成此操作后,您将看到如下的gt JSON:

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

      您提议的JSON具有额外的嵌套级别

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

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