Json.NET 변환기를 사용하여 속성 역 직렬화
인터페이스를 반환하는 속성을 포함하는 클래스 정의가 있습니다.
public class Foo
{
public int Number { get; set; }
public ISomething Thing { get; set; }
}
Json.NET을 사용하여 Foo 클래스를 직렬화하려고하면 " 'ISomething'유형의 인스턴스를 만들 수 없습니다. ISomething은 인터페이스 또는 추상 클래스 일 수 있습니다."와 같은 오류 메시지가 나타납니다.
Something
deserialization 중에 사용할 구체적인 클래스를 지정할 수있는 Json.NET 특성 또는 변환기가 있습니까?
Json.NET으로 할 수있는 작업 중 하나는 다음과 같습니다 .
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
이 TypeNameHandling
플래그는 $type
JSON에 속성을 추가하여 Json.NET이 개체를 역 직렬화해야하는 구체적인 유형을 알 수 있도록합니다. 이를 통해 인터페이스 또는 추상 기본 클래스를 수행하면서 객체를 역 직렬화 할 수 있습니다.
그러나 단점은 이것이 매우 Json.NET 전용이라는 것입니다. 는 $type
정규화 된 유형이므로 유형 정보로 직렬화하는 경우 deserializer도이를 이해할 수 있어야합니다.
문서 : Json.NET을 사용한 직렬화 설정
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; }
}
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();
}
}
Json.Net을 통해 역 직렬화 된 조직에서 작업 할 때 Owner 속성의 기본 IPerson은 Tycoon 유형이됩니다.
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; }
}
최신 버전의 타사 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;
}
}
Something이 ISomething을 구현하는 한 작동합니다. 또한 JSon 변환기가이를 사용하려는 경우 기본 빈 생성자를 넣지 마십시오. 구체적인 유형을 포함하는 생성자를 사용하도록 강제해야합니다.
추신. 이것은 또한 세터를 비공개로 만들 수 있습니다.
같은 문제가 있었기 때문에 알려진 유형 인수를 사용하는 내 변환기를 생각해 냈습니다.
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.
Normally I have always used the solution with TypeNameHandling
as suggested by DanielT, but in cases here I have not had control over the incoming JSON (and so cannot ensure that it includes a $type
property) I have written a custom converter that just allows you to explicitly specify the concrete 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.
The source code and an overview are available on this blog post.
I just wanted to complete the example that @Daniel T. showed us above:
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:
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 TypeNameHandling
flag:
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.
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.
Here is a reference to 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.
참고URL : https://stackoverflow.com/questions/2254872/using-json-net-converters-to-deserialize-properties
'program story' 카테고리의 다른 글
명령 출력에 쉘 스크립트의 특정 문자열이 포함되어 있는지 확인 (0) | 2020.09.18 |
---|---|
미국 달러 금액을 나타내는 데 사용하는 가장 좋은 장고 모델 필드는 무엇입니까? (0) | 2020.09.18 |
C #의 배열 크기 (길이) (0) | 2020.09.18 |
속성의 속성이 null인지 확인하는 C # 우아한 방법 (0) | 2020.09.18 |
jquery를 사용하여 라디오에서 이벤트를 클릭하거나 변경하십시오. (0) | 2020.09.18 |