Obtener ServiceStack para retener información de tipo

Estoy usando ServiceStack para serializar y deserializar algunos objetos a JSON. Considera este ejemplo:

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 

La última línea arroja una InvalidCastException, porque el campo Animal está instanciado como un tipo de Animal, no como un tipo de Perro. ¿Hay alguna manera en que pueda decirle a ServiceStack que retenga la información de que esta instancia particular era del tipo Perro?

La herencia en DTO es una mala idea: los DTO deben ser tan autodescriptivos como sea posible y, al usar herencia, los clientes no tienen idea de qué devuelve finalmente el servicio. Por eso, las clases de DTO no podrán serializarse correctamente en la mayoría de los serializadores basados ​​en estándares.

No hay una buena razón para tener interfaces en DTO (y muy pocas razones para tenerlas en modelos POCO), es un hábito de culto a la carga de usar interfaces para reducir el acoplamiento en el código de la aplicación que se está filtrando sin pensar en DTO. Pero a través de los límites del proceso, las interfaces solo agregan acoplamiento (solo se reduce en código) ya que el consumidor no tiene idea de qué tipo concreto se debe deserializar, por lo que debe emitir sugerencias de implementación específicas de serialización que ahora incrusta C # preocupaciones en el cable (por lo que Los espacios de nombres C # romperán la serialización) y ahora restringe su respuesta para ser utilizada por un serializador en particular. Las preocupaciones filtradas de C # en el cable violan uno de los objectives principales de los servicios para permitir la interoperabilidad.

Como no existe el concepto de “tipo de información” en la especificación JSON, para que la herencia funcione en los Serializadores JSON necesitan emitir extensiones propietarias al formato de cable JSON para incluir este tipo de información, que ahora combina su carga JSON con un JSON específico implementación del serializador.

ServiceStack’s JsonSerializer almacena este tipo de información en la propiedad __type y dado que puede hinchar considerablemente la carga útil, solo emitirá este tipo de información para los tipos que lo necesiten, es decir, Interfaces , tipos de object límite posterior o clases abstract .

Dicho esto, la solución sería cambiar a Animal para que sea una interfaz o una clase abstracta ; sin embargo, la recomendación es no usar herencia en DTO.

Estás serializando solo las propiedades del objeto animal, ya sea que el objeto serializado sea perro o no. Incluso si agrega una propiedad pública a la clase de perro, como “Nombre”, no se serializará, por lo que cuando se deserialice, solo tendrá propiedades de una clase “Animal”.

Si lo cambia a lo siguiente, funcionará;

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

Quizás esté fuera de tema, pero el serializador de Newtonsoft puede hacerlo incluyendo la opción:

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

Creará una propiedad dentro de json llamada $ type con el tipo fuerte del objeto. Cuando llamas al deserializador, ese valor se usará para construir el objeto nuevamente con los mismos tipos. La próxima prueba funciona usando newtonsoft con tipo fuerte, no con 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; } }