Получение 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?
- Поддерживает ли ServiceStack двоичные ответы?
- ServiceStack: RESTful Resource Versioning
- ServiceStack.Net Redis: сохранение связанных объектов и связанных с ними идентификаторов объектов
- ServiceStack и возврат streamа
- Страница настроек бритвы ServiceStack
Наследование в 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; } }