使用 Json.NET 转换器反序列化属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2254872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Json.NET converters to deserialize properties
提问by dthrasher
I have a class definition that contains a property that returns an interface.
我有一个包含返回接口的属性的类定义。
public class Foo
{
public int Number { get; set; }
public ISomething Thing { get; set; }
}
Attempting to serialize the Foo class using Json.NET gives me an error message like, "Could not create an instance of type 'ISomething'. ISomething may be an interface or abstract class."
尝试使用 Json.NET 序列化 Foo 类会给我一条错误消息,例如“无法创建类型为 'ISomething' 的实例。ISomething 可能是一个接口或抽象类。”
Is there a Json.NET attribute or converter that would let me specify a concrete Somethingclass to use during deserialization?
是否有 Json.NET 属性或转换器可以让我指定Something在反序列化期间使用的具体类?
回答by Daniel T.
One of the things you can do with Json.NETis:
您可以使用Json.NET做的一件事是:
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
The TypeNameHandlingflag will add a $typeproperty to the JSON, which allows Json.NET to know which concrete type it needs to deserialize the object into. This allows you to deserialize an object while still fulfilling an interface or abstract base class.
该TypeNameHandling标志将向$typeJSON添加一个属性,这允许 Json.NET 知道它需要将对象反序列化为哪种具体类型。这允许您反序列化一个对象,同时仍然满足一个接口或抽象基类。
The downside, however, is that this is very Json.NET-specific. The $typewill be a fully-qualified type, so if you're serializing it with type info,, the deserializer needs to be able to understand it as well.
然而,缺点是这是非常特定于 Json.NET 的。这$type将是一个完全限定的类型,所以如果你用类型信息序列化它,反序列化器也需要能够理解它。
Documentation: Serialization Settings with Json.NET
回答by MrMDavidson
You can achieve this through the use of the JsonConverter class. Suppose you have a class with an interface property;
您可以通过使用 JsonConverter 类来实现这一点。假设您有一个具有接口属性的类;
public class Organisation {
public string Name { get; set; }
[JsonConverter(typeof(TycoonConverter))]
public IPerson Owner { get; set; }
}
public interface IPerson {
string Name { get; set; }
}
public class Tycoon : IPerson {
public string Name { get; set; }
}
Your JsonConverter is responsible for serializing and de-serializing the underlying property;
你的 JsonConverter 负责序列化和反序列化底层属性;
public class TycoonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IPerson));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<Tycoon>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Left as an exercise to the reader :)
throw new NotImplementedException();
}
}
When you work with an Organisation deserialized via Json.Net the underlying IPerson for the Owner property will be of type Tycoon.
当您使用通过 Json.Net 反序列化的组织时,Owner 属性的底层 IPerson 将是 Tycoon 类型。
回答by Erhhung
Instead of passing a customized JsonSerializerSettings object to JsonConvert.SerializeObject() with the TypeNameHandling.Objects option, as previously mentioned, you can just mark that specific interface property with an attribute so the generated JSON wouldn't be bloated with "$type" properties on EVERY object:
如前所述,您无需使用 TypeNameHandling.Objects 选项将自定义的 JsonSerializerSettings 对象传递给 JsonConvert.SerializeObject(),您只需使用属性标记该特定接口属性,这样生成的 JSON 就不会因“$type”属性而变得臃肿在每个对象上:
public class Foo
{
public int Number { get; set; }
// Add "$type" property containing type info of concrete class.
[JsonProperty( TypeNameHandling = TypeNameHandling.Objects )]
public ISomething { get; set; }
}
回答by SamuelDavis
In the most recent version of the third party Newtonsoft Json converter you can set a constructor with a concrete type relating to the interfaced property.
在第三方 Newtonsoft Json 转换器的最新版本中,您可以使用与接口属性相关的具体类型设置构造函数。
public class Foo
{
public int Number { get; private set; }
public ISomething IsSomething { get; private set; }
public Foo(int number, Something concreteType)
{
Number = number;
IsSomething = concreteType;
}
}
As long as Something implements ISomething this should work. Also do not put a default empty constructor in case the JSon converter attempts to use that, you must force it to use the constructor containing the concrete type.
只要Something实现了ISomething,这应该可以工作。也不要放置默认的空构造函数,以防 JSon 转换器尝试使用它,您必须强制它使用包含具体类型的构造函数。
PS. this also allows you to make your setters private.
附注。这也允许您将 setter 设为私有。
回答by Bruno Altinet
Had a same problem so i came up with my own Converter which uses known types argument.
有同样的问题,所以我想出了我自己的转换器,它使用已知类型的参数。
public class JsonKnownTypeConverter : JsonConverter
{
public IEnumerable<Type> KnownTypes { get; set; }
public JsonKnownTypeConverter(IEnumerable<Type> knownTypes)
{
KnownTypes = knownTypes;
}
protected object Create(Type objectType, JObject jObject)
{
if (jObject["$type"] != null)
{
string typeName = jObject["$type"].ToString();
return Activator.CreateInstance(KnownTypes.First(x =>typeName.Contains("."+x.Name+",")));
}
throw new InvalidOperationException("No supported type");
}
public override bool CanConvert(Type objectType)
{
if (KnownTypes == null)
return false;
return (objectType.IsInterface || objectType.IsAbstract) && KnownTypes.Any(objectType.IsAssignableFrom);
}
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
var target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
I defined two extension methods for deserializing and serializing:
我定义了两个用于反序列化和序列化的扩展方法:
public static class AltiJsonSerializer
{
public static T DeserializeJson<T>(this string jsonString, IEnumerable<Type> knownTypes = null)
{
if (string.IsNullOrEmpty(jsonString))
return default(T);
return JsonConvert.DeserializeObject<T>(jsonString,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Converters = new List<JsonConverter>
(
new JsonConverter[]
{
new JsonKnownTypeConverter(knownTypes)
}
)
}
);
}
public static string SerializeJson(this object objectToSerialize)
{
return JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented,
new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto});
}
}
You can define your own way of comparing and identifying types in the convertes, i only use class name.
您可以定义自己的比较和识别类型转换的方式,我只使用类名。
回答by Steve Greatrex
Normally I have always used the solution with TypeNameHandlingas suggested by DanielT, but in cases here I have not had control over the incoming JSON (and so cannot ensure that it includes a $typeproperty) I have written a custom converter that just allows you to explicitly specify the concrete type:
通常我一直TypeNameHandling按照 DanielT 的建议使用解决方案,但在这里的情况下,我无法控制传入的 JSON(因此无法确保它包含$type属性)我编写了一个自定义转换器,它只允许您明确指定具体类型:
public class Model
{
[JsonConverter(typeof(ConcreteTypeConverter<Something>))]
public ISomething TheThing { get; set; }
}
This just uses the default serializer implementation from Json.Net whilst explicitly specifying the concrete type.
这仅使用来自 Json.Net 的默认序列化器实现,同时显式指定具体类型。
The source code and an overview are available on this blog post.
此博客文章中提供了源代码和概述。
回答by Luis Armando
I just wanted to complete the example that @Daniel T. showed us above:
我只是想完成上面@Daniel T. 向我们展示的示例:
If you are using this code to serialize your object:
如果您使用此代码序列化您的对象:
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
The code to deserialize the json should look like this:
反序列化 json 的代码应如下所示:
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
var entity = JsonConvert.DeserializeObject<EntityType>(json, settings);
This is how a json gets conformed when using the TypeNameHandlingflag:
回答by Timothy Baldridge
I've wondered this same thing, but I'm afraid it can't be done.
我也想过同样的事情,但恐怕做不到。
Let's look at it this way. You hand to JSon.net a string of data, and a type to deserialize into. What is JSON.net to do when it hit's that ISomething? It can't create a new type of ISomething because ISomething is not an object. It also can't create an object that implements ISomething, since it doesn't have a clue which of the many objects that may inherit ISomething it should use. Interfaces, are something that can be automatically serialized, but not automatically deserialized.
让我们这样看。您向 Json.net 传递一串数据和一个要反序列化的类型。当 JSON.net 遇到 ISomething 时,它会做什么?它无法创建新类型的 ISomething,因为 ISomething 不是对象。它也不能创建一个实现 ISomething 的对象,因为它不知道它应该使用哪些可能继承 ISomething 的对象。接口是可以自动序列化但不能自动反序列化的东西。
What I would do would be to look at replacing ISomething with a base class. Using that you might be able to get the effect you are looking for.
我会做的是考虑用基类替换 ISomething。使用它,您可能能够获得您正在寻找的效果。
回答by ram
Here is a referenceto an article written by ScottGu
Based on that, I wrote some code which I think might be helpful
基于此,我写了一些我认为可能有用的代码
public interface IEducationalInstitute
{
string Name
{
get; set;
}
}
public class School : IEducationalInstitute
{
private string name;
#region IEducationalInstitute Members
public string Name
{
get { return name; }
set { name = value; }
}
#endregion
}
public class Student
{
public IEducationalInstitute LocalSchool { get; set; }
public int ID { get; set; }
}
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int depth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = depth;
return serializer.Serialize(obj);
}
}
And this is how you would call it
这就是你会怎么称呼它
School myFavSchool = new School() { Name = "JFK High School" };
Student sam = new Student()
{
ID = 1,
LocalSchool = myFavSchool
};
string jSONstring = sam.ToJSON();
Console.WriteLine(jSONstring);
//Result {"LocalSchool":{"Name":"JFK High School"},"ID":1}
If I understand it correctly, I do not think you need to specify a concrete class which implements the interface for JSON serialization.
如果我理解正确,我认为您不需要指定一个具体的类来实现 JSON 序列化的接口。

