Получение ServiceStack для сохранения информации о типе

Я использую ServiceStack для сериализации и десериализации некоторых объектов в JSON. Рассмотрим этот пример:

public class Container { public Animal Animal { get; set; } } public class Animal { } public class Dog : Animal { public void Speak() { Console.WriteLine("Woof!"); } } var container = new Container { Animal = new Dog() }; var json = JsonSerializer.SerializeToString(container); var container2 = JsonSerializer.DeserializeFromString(json); ((Dog)container.Animal).Speak(); //Works ((Dog)container2.Animal).Speak(); //InvalidCastException 

Последняя строка генерирует InvalidCastException, потому что поле Animal создается как тип Animal, а не тип Dog. Есть ли способ сказать ServiceStack, чтобы сохранить информацию о том, что этот конкретный экземпляр относится к типу Dog?

Наследование в DTO – это плохая идея – DTO должны быть как можно более самоописательными, и, используя клиентов наследования, фактически не имеют представления о том, что сервис в конечном итоге возвращает. Именно поэтому ваши classы DTO не смогут правильно сериализоваться в большинстве «сериализаторов на основе стандартов».

Нет никакой веской причины иметь интерфейсы в DTO (и очень немногие причины, чтобы иметь их на моделях POCO), это кустарная привычка использовать интерфейсы для уменьшения связи в коде приложения, который бездумно просачивается в DTO. Но через границы процесса интерфейсы добавляют только сцепление (оно только сокращено в коде), так как потребитель не имеет представления о том, какой конкретный тип десериализуется, поэтому он должен испускать намеки на реализацию сериализации, которые теперь встраивают проблемы C # на провод (так что теперь даже Пространства имен C # прервут сериализацию) и теперь ограничивает ваш ответ для использования определенным сериализатором. Утечка проблем с C # на проводе нарушает одну из основных целей служб для обеспечения совместимости.

Поскольку в спецификации JSON нет понятия «информация о типе», чтобы наследование работало в сериализаторах JSON, им необходимо выпустить проприетарные расширения для JSON wireformat, чтобы включить эту информацию типа, которая теперь связывает вашу полезную нагрузку JSON с конкретным JSON сериализатор.

Служба JsonSerializer ServiceStack хранит информацию этого типа в свойстве __type и, поскольку она может значительно раздуть полезную нагрузку, будет генерировать информацию этого типа только для типов, которые в ней нуждаются, то есть Interfaces , поздних типов object или abstract classов.

С учетом сказанного решение заключается в том, чтобы изменить Animal как интерфейс или абстрактный class, однако рекомендация не должна использовать наследование в DTO.

Вы сериализуете только свойства объекта животного, независимо от того, является ли сериализованный объект собакой или нет. Даже если вы добавите публичное свойство в class собаки, например «Имя», оно не будет сериализовано, поэтому при десериализации вы будете иметь только свойства classа «Животное».

Если вы измените его на следующее, это будет работать;

 public class Container where T: Animal { public T Animal { get; set; } } public class Animal { } public class Dog : Animal { public void Speak() { Console.WriteLine("Woof!"); } public string Name { get; set; } } var c = new Container { Animal = new Dog() { Name = "dog1" } }; var json = JsonSerializer.SerializeToString>(c); var c2 = JsonSerializer.DeserializeFromString>(json); c.Animal.Speak(); //Works c2.Animal.Speak(); 

Может быть, вне темы, но сериализатор Newtonsoft может это сделать, включая опцию:

  serializer = new JsonSerializer(); serializer.TypeNameHandling = TypeNameHandling.All; 

Он создаст свойство внутри json с именем $ type с сильным типом объекта. Когда вы вызываете десериализатор, это значение будет использоваться для повторного создания объекта с теми же типами. Следующий тест работает с использованием newtonsoft с сильным типом, а не с ServiceStack

  [TestFixture] public class ServiceStackTests { [TestCase] public void Foo() { FakeB b = new FakeB(); b.Property1 = "1"; b.Property2 = "2"; string raw = b.ToJson(); FakeA a=raw.FromJson(); Assert.IsNotNull(a); Assert.AreEqual(a.GetType(), typeof(FakeB)); } } public abstract class FakeA { public string Property1 { get; set; } } public class FakeB:FakeA { public string Property2 { get; set; } } 
Давайте будем гением компьютера.