JSON.Net序列化派生类

本文关键字:派生 序列化 Net JSON | 更新日期: 2023-09-27 18:08:19

要使用我正在开发的菜谱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 ?
Group<string, Ingredient> 

Group<string, string>

?

JSON.Net序列化派生类

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

你有两种方法来处理这个问题:

  1. 编写自己的自定义JsonConverter。您可以使用Json中的反射来确定泛型参数。

  2. Group<K, T>标记为[JsonObject]

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

[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参数为空,它的单个参数化抛出一个NullReferenceException。在没有默认构造函数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

完成后,您将得到如下JSON:

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

您建议的JSON与

有额外的嵌套级别
{
"IngredientsWithHeaders": [
    {
        "Group": {
            "Header": "BlankHeader",

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