简体   繁体   English

JSON.Net序列化派生类

[英]JSON.Net Serializing Derived Classes

To work with a recipe webservice I'm developing, I have the following classes to hold and serialize recipe data: 为了使用我正在开发的配方网络服务,我具有以下类来保存和序列化配方数据:

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);
    }
}    

The JSON output I am getting for the List<Group<string, Ingredient>> is 我为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"
        }
        ]
    ]
}

and what I would like to be getting is more along the lines of 我想得到的更多是

{
"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"
                }
                ]
            }
        }
    ]
}

Do I need to write a custom serializer? 我需要编写自定义序列化程序吗? If so, how do I go about casting an object to a parameterized Group without knowing if it is 如果是这样,我该如何在不知道对象是否为对象的情况下将其强制转换为参数化的组

Group<string, Ingredient> 

or 要么

Group<string, string>

?

The issue here is that your Group<K, T> is a collection that also has properties. 这里的问题是,您的Group<K, T>是一个也具有属性的集合。 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容器可以是数组(无属性)或对象(具有命名的键/值对),因此具有自定义属性的集合不能自动映射到其中一个而不会丢失数据。 Json.NET (and all other serializers AFAIK) choose to map the items not the custom properties. Json.NET(和所有其他序列化程序AFAIK)选择映射项而不是自定义属性。

You have a couple ways to deal with this: 您有几种方法可以解决此问题:

  1. Write your own custom JsonConverter . 编写自己的自定义JsonConverter You can determine the generic arguments using reflection along the lines of Json.Net returns Empty Brackets . 您可以使用Json.Net返回Empty Brackets的路线来确定泛型参数。

  2. Mark your Group<K, T> with [JsonObject] . [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;
    }
}

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. 顺便提及,您还有另一个问题-您的Ingredient类没有默认的构造函数,并且如果line参数为null,则其单个参数化将引发NullReferenceException In the absence of a default constructor Json.NET will call the single parameterized constructor, mapping JSON object values to constructor arguments by name. 在没有默认构造函数的情况下,Json.NET将调用单个参数化的构造函数,并按名称将JSON对象值映射到构造函数参数。 Thus, deserialization throws an exception. 因此,反序列化引发异常。

You have a few ways to deal with this: 您有几种方法可以解决此问题:

  1. Add a public default constructor. 添加一个公共默认构造函数。

  2. Add a private default constructor and mark it with [JsonConstructor] : 添加一个私有的默认构造函数,并用[JsonConstructor]标记它:

     [JsonConstructor] Ingredient() { } 
  3. Add a private default constructor and deserialize with ConstructorHandling.AllowNonPublicDefaultConstructor : 添加一个私有默认构造函数并使用ConstructorHandling.AllowNonPublicDefaultConstructor反序列化:

     var settings = new JsonSerializerSettings { ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor }; var recipe = JsonConvert.DeserializeObject<Recipe>(json, settings); 
  4. Add an if (line != null) check in the constructor. 在构造函数中添加一个if (line != null)检查。 (Not really recommended. Instead your constructor should explicitly throw an ArgumentNullException .) (不建议这样做。相反,构造函数应显式抛出ArgumentNullException 。)

Having done this, you will gt JSON that looks like: 完成此操作后,您将看到如下的gt JSON:

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

Your proposed JSON has an extra level of nesting with 您建议的JSON具有额外的嵌套层次

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

This extra "Group" object is unnecessary. 此多余的"Group"对象是不必要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM