Использование преобразователей Json.NET для десериализации свойств

У меня есть определение classа, которое содержит свойство, которое возвращает интерфейс.

public class Foo { public int Number { get; set; } public ISomething Thing { get; set; } } 

Попытка сериализации classа Foo с использованием Json.NET дает мне сообщение об ошибке вроде «Не удалось создать экземпляр типа« ISomething ». ISomething может быть интерфейсом или абстрактным classом».

Есть ли атрибут или конвертер Json.NET, который позволит мне указать конкретный class Something для использования во время десериализации?

Одна из вещей, которые вы можете сделать с Json.NET :

 var settings = new JsonSerializerSettings(); settings.TypeNameHandling = TypeNameHandling.Objects; JsonConvert.SerializeObject(entity, Formatting.Indented, settings); 

Флаг TypeNameHandling добавит свойство $type в JSON, что позволяет Json.NET знать, какой конкретный тип ему нужен для десериализации объекта. Это позволяет десериализовать объект, сохраняя при этом интерфейс или абстрактный базовый class.

Однако недостатком является то, что это очень специфичный Json.NET. Тип $type будет полностью квалифицированным, поэтому, если вы сериализуете его с информацией о типе, десериализатор должен уметь это понимать.

Документация: настройки Serialization с помощью Json.NET

Вы можете добиться этого с помощью classа JsonConverter. Предположим, у вас есть class с свойством интерфейса;

 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(reader); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // Left as an exercise to the reader :) throw new NotImplementedException(); } } 

Когда вы работаете с организацией, десериализованной через Json.Net, базовое свойство IPerson для владельца будет иметь тип Tycoon.

Вместо передачи настраиваемого объекта JsonSerializerSettings в JsonConvert.SerializeObject () с параметром TypeNameHandling.Objects, как уже упоминалось ранее, вы можете просто отметить это свойство интерфейса атрибутом, чтобы сгенерированный 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; } } 

Пока что-то реализует ISomething, это должно работать. Также не ставьте пустой конструктор по умолчанию, если JSon-конвертер пытается его использовать, вы должны заставить его использовать конструктор, содержащий конкретный тип.

PS. это также позволяет сделать ваши сеттеры частными.

Имела та же проблема, поэтому я придумал свой собственный конвертер, который использует аргументы известных типов.

 public class JsonKnownTypeConverter : JsonConverter { public IEnumerable KnownTypes { get; set; } public JsonKnownTypeConverter(IEnumerable 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(); } } 

Я определил два метода расширения для десериализации и сериализации:

 public static class AltiJsonSerializer { public static T DeserializeJson(this string jsonString, IEnumerable knownTypes = null) { if (string.IsNullOrEmpty(jsonString)) return default(T); return JsonConvert.DeserializeObject(jsonString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Converters = new List ( new JsonConverter[] { new JsonKnownTypeConverter(knownTypes) } ) } ); } public static string SerializeJson(this object objectToSerialize) { return JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}); } } 

Вы можете определить свой собственный способ сравнения и идентификации типов в конвертах, я использую только имя classа.

Обычно я всегда использовал решение с помощью TypeNameHandling как было предложено DanielT, но в таких случаях я не контролировал входящий JSON (и поэтому не могу гарантировать, что он включает свойство $type ). Я написал собственный конвертер, который просто позволяет вам для конкретного указания конкретного типа:

 public class Model { [JsonConverter(typeof(ConcreteTypeConverter))] public ISomething TheThing { get; set; } } - public class Model { [JsonConverter(typeof(ConcreteTypeConverter))] public ISomething TheThing { get; set; } } 

Это просто использует реализацию сериализатора по умолчанию от Json.Net, явно указывая конкретный тип.

Исходный код и обзор доступны в этом блоге .

Я просто хотел привести пример, который показал нам @Daniel T.:

Если вы используете этот код для сериализации вашего объекта:

 var settings = new JsonSerializerSettings(); settings.TypeNameHandling = TypeNameHandling.Objects; JsonConvert.SerializeObject(entity, Formatting.Indented, settings); 

Код десериализации json должен выглядеть следующим образом:

 var settings = new JsonSerializerSettings(); settings.TypeNameHandling = TypeNameHandling.Objects; var entity = JsonConvert.DeserializeObject(json, settings); 

Вот как json получает соответствие при использовании флага TypeNameHandling : введите описание изображения здесь

Я задавался вопросом об этом же, но, боюсь, этого не может быть сделано.

Давайте посмотрим на это так. Вы передаете JSon.net строку данных и тип десериализации. Что делает JSON.net, когда он попадает в этот ISomething? Он не может создать новый тип ISomething, потому что ISomething не является объектом. Он также не может создать объект, который реализует ISomething, поскольку он не имеет понятия, какой из многих объектов может наследовать ISomething, который он должен использовать. Интерфейсы – это то, что можно автоматически сериализовать, но не автоматически десериализовать.

То, что я сделал бы, – это посмотреть на замену ISomething базовым classом. Используя это, вы сможете получить эффект, который вы ищете.

Вот ссылка на статью, написанную ScottGu

Исходя из этого, я написал код, который, я думаю, может быть полезен

 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); } } 

И так вы бы назвали это

 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} 

Если я правильно понимаю, я не думаю, что вам нужно указать конкретный class, который реализует интерфейс для сериализации JSON.

  • Как получить json.net для сериализации членов classа, полученных из List ?
  • Почему, когда конструктор аннотируется с помощью @JsonCreator, его аргументы должны быть аннотированы с помощью @JsonProperty?
  • Ошибка JSON.NET Локальный цикл привязки для типа
  • Сериализовать статический class?
  • Как изменить имена свойств при сериализации с помощью Json.net?
  • Невозможно сериализовать словарь с помощью сложного ключа с помощью Json.net
  • JSON.Net Обнаружен собственный цикл привязки
  • Как десериализовать объект JObject для .NET
  • GSON десериализует значение ключа для пользовательского объекта
  • C # десериализация структуры после ее получения через TCP
  • Как сериализовать объект в CSV-файл?
  • Давайте будем гением компьютера.