有没有办法JSON.NET-序列化List 的子类,还有额外的属性?

好的,我们正在使用Newtonsoft的JSON.NET产品,我非常喜欢。 但是,我有一个简单的类结构用于层次结构位置,看起来大致像这样……

public class Location { public string Name{ get; set; } public LocationList Locations{ get; set; } } // Note: LocationList is simply a subclass of a List // which then adds an IsExpanded property for use by the UI. public class LocationList : List { public bool IsExpanded{ get; set; } } public class RootViewModel { public LocationList RootLocations{ get; set; } } 

…当我将它们序列化为JSON时,一切都很好,除了排除了LocationList类的IsExpanded属性。 只列出了列表的内容。

现在,我想象的将是一个很好的格式。 它实际上与LocationList不是List的子类相同,而只是一个常规对象,它具有一个名为Items类型为List的属性。

 { "Locations": { "IsExpanded": true, "Items": [ { "Name": "Main Residence", "Locations": { "IsExpanded": true, "Items": [ { "Name": "First Floor", "Locations": { "IsExpanded": false, "Items": [ { "Name": "Livingroom" }, { "Name": "Dining Room" }, { "Name": "Kitchen" } ] }, { "Name": "Second Floor", "Locations": { "IsExpanded": false, "Items": [ { "Name": "Master Bedroom" }, { "Name": "Guest Bedroom" } ] }, { "Name": "Basement" } ] } } ] } } 

现在我也明白,Newtonsoft的产品是可扩展的,因为它们专门讨论了如何为特定数据类型编写自己的自定义序列化程序,这正是我想要的。 但是,他们没有任何关于如何执行此操作的良好代码示例。

如果我们(SO社区)可以解决这个问题,从技术上讲,通过使用上面的格式,我们应该能够序列化List的任何子类(或其衍生物/类似对象),前提是它们还没有名为Items的属性(恕我直言)首先是一个糟糕的设计,因为它会像垃圾一样令人困惑!)也许我们甚至可以让Newtonsoft在他们的序列化器中本地推出这样的东西!

所以说……有人知道如何自定义序列化器/反序列化器来区别对待这个对象吗?

中号

通常当我发现自己在做这样的事情时,它告诉我应该考虑另一种方法。 在这种情况下,我建议使用以下视图模型结构作为替代方案:

 public class Location { public bool IsExpanded { get; set; } public string Name { get; set; } public List Locations { get; set; } } public class ViewModel { public List RootLocations { get; set; } } 

好的……所以这就是我想出来的。 我不得不写自己的JsonConverter。 我基本上用它来创建一个内联JObject,它具有我希望它们持久化的属性结构,然后我坚持这一点。 然后,当我把它读出来时,我会反过来。

然而,下面的是它不使用reflection或任何其他这样的东西,所以这只适用于我必须通过属性手工编码属性的这种特定类型(在这种情况下,只有两个,所以这很好!)和它也没有利用DefaultValues处理,我必须手动重新模拟,这意味着基本上忽略了属性,除非我反思它们。 不过,这仍然有效。 完善? 不,但是嘿……事情很少发生!

当然,欢迎并鼓励评论!

 public class LocationListJsonConverter : JsonConverter { public override bool CanConvert(System.Type objectType) { return objectType == typeof(LocationList); } public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer) { var locationList = (existingValue as LocationList) ?? new LocationList(); var jLocationList = JObject.ReadFrom(reader); locationList.IsExpanded = (bool)(jLocationList["IsExpanded"] ?? false); var jLocations = jLocationList["_Items"]; if(jLocations != null) { foreach(var jLocation in jLocations) { var location = serializer.Deserialize(new JTokenReader(jLocation)); locationList.Add(location); } } return locationList; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var locationList = value as LocationList; JObject jLocationList = new JObject(); if(locationList.IsExpanded) jLocationList.Add("IsExpanded", true); if(locationList.Count > 0) { var jLocations = new JArray(); foreach(var location in locationList) { jLocations.Add(JObject.FromObject(location, serializer)); } jLocationList.Add("_Items", jLocations); } jLocationList.WriteTo(writer); } }