我正在尝试扩展这里
给出的 JSON.net 示例 http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
我有另一个从基类/接口派生的子类
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
List<Person> people = new List<Person>
{
new Employee(),
new Employee(),
new Artist(),
};
如何反序列化以下 Json 回到列表<人员>
[
{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},
{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{
"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}
]
我不想使用 TypeNameHandling JsonSerializerSettings。我专门寻找自定义 JsonConverter 实现来处理这个问题。围绕这一点的文档和示例在网上非常稀少。我似乎无法正确获得 JsonConverter 中被覆盖的 ReadJson() 方法实现。
使用标准,我一直在努力研究如何生成正确的类型(或),因为为了确定这一点,您需要分析 JSON,并且没有内置的方法可以使用该方法执行此操作。CustomCreationConverter
Person
Employee
Create
我找到了一个与类型转换相关的讨论线程,结果它提供了答案。这是一个链接:类型转换(存档链接)。
所需要的是子类,覆盖该方法并创建一个接受.JsonConverter
ReadJson
Create
JObject
JObject 类提供了一种加载 JSON 对象的方法,并提供
对该对象中数据的访问。
重写的方法创建一个并调用该方法(由我们的派生转换器类实现),传入实例。ReadJson
JObject
Create
JObject
然后,可以通过检查某些字段是否存在来分析此实例以确定正确的类型。JObject
例
string json = "[{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}]";
List<Person> persons =
JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());
...
public class PersonConverter : JsonCreationConverter<Person>
{
protected override Person Create(Type objectType, JObject jObject)
{
if (FieldExists("Skill", jObject))
{
return new Artist();
}
else if (FieldExists("Department", jObject))
{
return new Employee();
}
else
{
return new Person();
}
}
private bool FieldExists(string fieldName, JObject jObject)
{
return jObject[fieldName] != null;
}
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>
/// Create an instance of objectType, based properties in the JSON object
/// </summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">
/// contents of JSON object that will be deserialized
/// </param>
/// <returns></returns>
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
}
上述解决方案遍布互联网,但存在一个缺陷,在极少数情况下会表现出来。在 ReadJson 方法中创建的新 JsonReader 不会继承任何原始读取器的配置值(Culture、DateParseHandling、DateTimeZoneHandling、FloatParseHandling 等)。在序列化程序中使用新的 JsonReader 之前,应复制这些值。填充()。JsonCreationConverter<T>
这是我能想到的解决上述实现的一些问题的最好方法,但我仍然认为有些事情被忽略了:
更新我更新了它以使用更明确的方法来复制现有阅读器。这只是封装了复制单个 JsonReader 设置的过程。理想情况下,此函数将保留在 Newtonsoft 库本身中,但现在,您可以使用以下内容:
/// <summary>Creates a new reader for the specified jObject by copying the settings
/// from an existing reader.</summary>
/// <param name="reader">The reader whose settings should be copied.</param>
/// <param name="jToken">The jToken to create a new reader for.</param>
/// <returns>The new disposable reader.</returns>
public static JsonReader CopyReaderForObject(JsonReader reader, JToken jToken)
{
JsonReader jTokenReader = jToken.CreateReader();
jTokenReader.Culture = reader.Culture;
jTokenReader.DateFormatString = reader.DateFormatString;
jTokenReader.DateParseHandling = reader.DateParseHandling;
jTokenReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
jTokenReader.FloatParseHandling = reader.FloatParseHandling;
jTokenReader.MaxDepth = reader.MaxDepth;
jTokenReader.SupportMultipleContent = reader.SupportMultipleContent;
return jTokenReader;
}
这应该按如下方式使用:
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
using (JsonReader jObjectReader = CopyReaderForObject(reader, jObject))
{
serializer.Populate(jObjectReader, target);
}
return target;
}
较旧的解决方案如下:
/// <summary>Base Generic JSON Converter that can help quickly define converters for specific types by automatically
/// generating the CanConvert, ReadJson, and WriteJson methods, requiring the implementer only to define a strongly typed Create method.</summary>
public abstract class JsonCreationConverter<T> : JsonConverter
{
/// <summary>Create an instance of objectType, based properties in the JSON object</summary>
/// <param name="objectType">type of object expected</param>
/// <param name="jObject">contents of JSON object that will be deserialized</param>
protected abstract T Create(Type objectType, JObject jObject);
/// <summary>Determines if this converted is designed to deserialization to objects of the specified type.</summary>
/// <param name="objectType">The target type for deserialization.</param>
/// <returns>True if the type is supported.</returns>
public override bool CanConvert(Type objectType)
{
// FrameWork 4.5
// return typeof(T).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
// Otherwise
return typeof(T).IsAssignableFrom(objectType);
}
/// <summary>Parses the json to the specified type.</summary>
/// <param name="reader">Newtonsoft.Json.JsonReader</param>
/// <param name="objectType">Target type.</param>
/// <param name="existingValue">Ignored</param>
/// <param name="serializer">Newtonsoft.Json.JsonSerializer to use.</param>
/// <returns>Deserialized Object</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
//Create a new reader for this jObject, and set all properties to match the original reader.
JsonReader jObjectReader = jObject.CreateReader();
jObjectReader.Culture = reader.Culture;
jObjectReader.DateParseHandling = reader.DateParseHandling;
jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
jObjectReader.FloatParseHandling = reader.FloatParseHandling;
// Populate the object properties
serializer.Populate(jObjectReader, target);
return target;
}
/// <summary>Serializes to the specified type</summary>
/// <param name="writer">Newtonsoft.Json.JsonWriter</param>
/// <param name="value">Object to serialize.</param>
/// <param name="serializer">Newtonsoft.Json.JsonSerializer to use.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
只是想我会分享一个解决方案,也基于此使用反射与 Knowntype 属性一起使用,必须从任何基类中获取派生类,解决方案可以从递归中受益以找到最佳匹配类,尽管我不需要它在我的情况下,匹配是由提供给转换器的类型完成的,如果它有 KnownType,它将扫描它们,直到它匹配具有所有JSON 字符串中的属性,将选择第一个匹配的属性。
用法非常简单:
string json = "{ Name:"Something", LastName:"Otherthing" }";
var ret = JsonConvert.DeserializeObject<A>(json, new KnownTypeConverter());
在上述情况下,RET 将是 B 型。
JSON类:
[KnownType(typeof(B))]
public class A
{
public string Name { get; set; }
}
public class B : A
{
public string LastName { get; set; }
}
转换器代码:
/// <summary>
/// Use KnownType Attribute to match a divierd class based on the class given to the serilaizer
/// Selected class will be the first class to match all properties in the json object.
/// </summary>
public class KnownTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return System.Attribute.GetCustomAttributes(objectType).Any(v => v is KnownTypeAttribute);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(objectType); // Reflection.
// Displaying output.
foreach (System.Attribute attr in attrs)
{
if (attr is KnownTypeAttribute)
{
KnownTypeAttribute k = (KnownTypeAttribute) attr;
var props = k.Type.GetProperties();
bool found = true;
foreach (var f in jObject)
{
if (!props.Any(z => z.Name == f.Key))
{
found = false;
break;
}
}
if (found)
{
var target = Activator.CreateInstance(k.Type);
serializer.Populate(jObject.CreateReader(),target);
return target;
}
}
}
throw new ObjectNotFoundException();
// Populate the object properties
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
模板简介:该模板名称为【C# > 如何在 JSON.NET 中实现自定义 JsonConverter?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【C#】栏目查找您需要的精美模板。