Сериализация типа Json.Net с полиморфным дочерним объектом

Мы хотели бы иметь возможность сериализовать / десериализовать json из / в classы C #, причем основной class имеет экземпляр полиморфного дочернего объекта. Сделать это легко, используя настройку типа Json.Net TypeNameHandling.Auto. Однако мы хотели бы сделать это без поля «$ type».

Первая мысль состоит в том, чтобы иметь возможность переименовать «$ type» в значение нашего выбора и иметь значение для типа be enum, которое правильно отображало бы подтипы. Я не видел это как вариант, но был бы рад услышать, если это возможно.

Вторая мысль шла по следующим строкам … Ниже представлен первый проход в classах, причем class верхнего уровня имеет индикатор (SubTypeType) относительно того, какой тип данных содержится в дочернем объекте (SubTypeData). Я немного подкопался в документации Json.Net и попробовал несколько вещей, но не повезло.

В настоящее время мы полностью контролируем определение данных, но после его развертывания все блокируется.

public class MainClass { public SubType SubTypeType { get; set; } public SubTypeClassBase SubTypeData { get; set; } } public class SubTypeClassBase { } public class SubTypeClass1 : SubTypeClassBase { public string AaaField { get; set; } } public class SubTypeClass2 : SubTypeClassBase { public string ZzzField { get; set; } } 

Наличие информации о подтипе в classе контейнера проблематично по двум причинам:

  1. Экземпляр classа контейнера недоступен, когда Json.NET читает содержащийся class.
  2. Если позже вам нужно будет преобразовать свойство SubTypeClassBase в, скажем, список, некуда будет помещать информацию о подтипе.

Вместо этого я бы рекомендовал добавить информацию подтипа в качестве свойства в базовом classе:

 [JsonConverter(typeof(SubTypeClassConverter))] public class SubTypeClassBase { [JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value public SubType Type { get { return typeToSubType[GetType()]; } } } 

Теперь настраиваемый подтип подтипа будет сериализован всякий раз, когда объект, назначаемый SubTypeClassBase , сериализуется. Сделав это, для десериализации вы можете создать JsonConverter который загружает json для данной SubTypeClassBase во временный JObject , проверяет значение свойства "Type" и десериализует объект JSON как соответствующий class.

Реализация прототипа ниже:

 public enum SubType { BaseType, Type1, Type2, } [JsonConverter(typeof(SubTypeClassConverter))] public class SubTypeClassBase { static readonly Dictionary typeToSubType; static readonly Dictionary subTypeToType; static SubTypeClassBase() { typeToSubType = new Dictionary() { { typeof(SubTypeClassBase), SubType.BaseType }, { typeof(SubTypeClass1), SubType.Type1 }, { typeof(SubTypeClass2), SubType.Type2 }, }; subTypeToType = typeToSubType.ToDictionary(pair => pair.Value, pair => pair.Key); } public static Type GetType(SubType subType) { return subTypeToType[subType]; } [JsonConverter(typeof(StringEnumConverter))] // Serialize enums by name rather than numerical value public SubType Type { get { return typeToSubType[GetType()]; } } } public class SubTypeClass1 : SubTypeClassBase { public string AaaField { get; set; } } public class SubTypeClass2 : SubTypeClassBase { public string ZzzField { get; set; } } public class SubTypeClassConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(SubTypeClassBase); } public override bool CanWrite { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var token = JToken.Load(reader); var typeToken = token["Type"]; if (typeToken == null) throw new InvalidOperationException("invalid object"); var actualType = SubTypeClassBase.GetType(typeToken.ToObject(serializer)); if (existingValue == null || existingValue.GetType() != actualType) { var contract = serializer.ContractResolver.ResolveContract(actualType); existingValue = contract.DefaultCreator(); } using (var subReader = token.CreateReader()) { // Using "populate" avoids infinite recursion. serializer.Populate(subReader, existingValue); } return existingValue; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

Вы можете попробовать с реализацией конвертера JsonSubtypes, которые поддерживают сопоставление типов регистрации с значениями enums.

В вашем случае это выглядит так:

  public class MainClass { public SubTypeClassBase SubTypeData { get; set; } } [JsonConverter(typeof(JsonSubtypes), "SubTypeType")] [JsonSubtypes.KnownSubType(typeof(SubTypeClass1), SubType.WithAaaField)] [JsonSubtypes.KnownSubType(typeof(SubTypeClass2), SubType.WithZzzField)] public class SubTypeClassBase { public SubType SubTypeType { get; set; } } public class SubTypeClass1 : SubTypeClassBase { public string AaaField { get; set; } } public class SubTypeClass2 : SubTypeClassBase { public string ZzzField { get; set; } } public enum SubType { WithAaaField, WithZzzField } [TestMethod] public void Deserialize() { var obj = JsonConvert.DeserializeObject("{\"SubTypeData\":{\"ZzzField\":\"zzz\",\"SubTypeType\":1}}"); Assert.AreEqual("zzz", (obj.SubTypeData as SubTypeClass2)?.ZzzField); } 

Вот полный пример, который может читать и записывать JSON с полиморфными объектами.

Предполагая, что мы имеем следующую структуру classов:

 public class Base {} public class SubClass1 : Base { public int field1; } public class SubClass2 : Base { public int field2; } 

Мы можем использовать настраиваемый конвертер, который создает дополнительное поле в JSON-названном type при сериализации и считывает его при десериализации.

 public class PolymorphicJsonConverter : JsonConverter { public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject item = JObject.Load(reader); var type = item["type"].Value(); if (type == "SubClass1") { return item.ToObject(); } else if (type == "SubClass2") { return item.ToObject(); } else { return null; } } public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer) { JObject o = JObject.FromObject(value); if (value is SubClass1) { o.AddFirst(new JProperty("type", new JValue("SubClass1"))); } else if (value is SubClass1) { o.AddFirst(new JProperty("type", new JValue("SubClass2"))); } o.WriteTo(writer); } public override bool CanConvert (Type objectType) { return typeof(Base).IsAssignableFrom(objectType); } } 

Вы можете использовать этот конвертер в classе контейнера, например:

 public class Container { public List items; public string Save() { return JsonConvert.SerializeObject(items, new PolymorphicJsonConverter()) } public void Load(string jsonText) { items = JsonConvert.DeserializeObject>(jsonText, new PolymorphicJsonConverter()); } } 

В качестве альтернативы вы можете использовать встроенный тип встроенного типа JSON.net, вместо того, чтобы запускать собственный JsonConverter, но он не настолько гибкий и создает очень не портативный JSON.

 JsonConvert.SerializeObject(items, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); 
  • Игнорирование нулевых полей в Json.net
  • Невозможно сериализовать словарь с помощью сложного ключа с помощью Json.net
  • Существуют ли эквиваленты C ++ для функций протокола ввода-вывода протокола Buffer в Java?
  • Может ли JavaScriptSerializer исключать свойства с нулевыми значениями / значениями по умолчанию?
  • Как превратить объект C # в строку JSON в .NET?
  • Как настроить сериализацию списка объектов JAXB для JSON?
  • Преобразование байтов в Int / uint в C
  • Сериализация JSON массива с полиморфными объектами
  • Как сериализовать Java-примитивы с помощью Jersey REST
  • Получение ServiceStack для сохранения информации о типе
  • Добавление к ObjectOutputStream
  • Interesting Posts

    как включить или выключить динамик в android 4.0

    Mac Mini HDMI-to-DVI для VGA-монитора?

    Файл поступает с другого компьютера и может быть заблокирован

    Как правильно писать код C / C ++, когда нулевой указатель не все биты нуль

    Параметры по умолчанию с конструкторами C ++

    Как удалить папку с внешних дисков, если имена слишком длинны (например, информация или контент тома системы)?

    Рекурсивный список сглаживания

    Как я могу зарегистрировать глобальный настраиваемый редактор в Spring-MVC?

    Виндзор – вытягивание Транзитные объекты из контейнера

    Как я могу заставить LINQ вернуть объект с максимальным значением для данного свойства?

    Создание ограниченной учетной записи пользователя в Windows 7

    Как включить несколько аудиовыходов в Windows 7?

    Rails 3.2: нажатие Heroku отклонено, приложение, поддерживающее Cedar, не обнаружено

    Как получить доступ к файлам s3a: // из Apache Spark?

    Утилита «find» не выводит все файлы при использовании подстановочных знаков

    Давайте будем гением компьютера.