JSON.Net序列化派生类 [英] JSON.Net Serializing Derived Classes
问题描述
要使用我正在开发的配方Web服务,我具有以下类来保存和序列化配方数据:
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);
}
}
我为List<Group<string, Ingredient>>
获得的JSON输出是
The JSON output I am getting for the List<Group<string, Ingredient>>
is
{
"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"
}
]
}
}
]
}
我需要编写自定义序列化程序吗?如果是这样,我该如何在不知道对象是否为
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>
或
Group<string, string>
?
推荐答案
这里的问题是您的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:
-
编写您自己的自定义
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:
-
添加公共默认构造函数.
Add a public default constructor.
Add a private default constructor and mark it with [JsonConstructor]
:
[JsonConstructor]
Ingredient() { }
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.
这篇关于JSON.Net序列化派生类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!