Conseguir que ServiceStack conserve información de tipo

Resuelto larspars asked hace 12 años • 3 respuestas

Estoy usando ServiceStack para serializar y deserializar algunos objetos en JSON. Considere 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<Container>(json);

((Dog)container.Animal).Speak(); //Works
((Dog)container2.Animal).Speak(); //InvalidCastException

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

larspars avatar May 25 '12 14:05 larspars
Aceptado

La herencia en las DTO es una mala idea: las DTO deben ser lo más autodescriptivas posible y, al utilizar la herencia, los clientes no tienen idea de lo que finalmente devuelve el servicio. Es por eso que sus clases DTO no podrán deserializarse/serializarse correctamente en la mayoría de los serializadores "basados ​​en estándares".

No hay una buena razón para tener interfaces en los DTO (y muy pocas razones para tenerlas en los modelos POCO), es un hábito de culto de uso de interfaces para reducir el acoplamiento en el código de la aplicación que se filtra irreflexivamente en los DTO. Pero a través de los límites del proceso, las interfaces solo agregan acoplamiento (solo se reduce en el código), ya que el consumidor no tiene idea de en qué tipo concreto deserializar, por lo que tiene que emitir sugerencias de implementación específicas de serialización que ahora incorporan preocupaciones de C# en el cable (por lo que ahora incluso Los espacios de nombres de C# interrumpirán la serialización) y ahora restringe su respuesta para que la utilice un serializador en particular. La filtración de preocupaciones de C# en el cable viola uno de los objetivos principales de los servicios para permitir la interoperabilidad.

Como no existe el concepto de 'información de tipo' en la especificación JSON, para que la herencia funcione en los serializadores JSON, deben emitir extensiones patentadas al formato de cable JSON para incluir este tipo de información, que ahora acopla su carga útil JSON a un JSON específico. Implementación del serializador.

JsonSerializer de ServiceStack almacena esta información de tipo en la propiedad __type y, dado que puede aumentar considerablemente la carga útil, solo emitirá esta información de tipo para los tipos que la necesitan, es decir , tipos o clases Interfacesde enlace tardío .objectabstract

Dicho esto, la solución sería cambiar Animala una interfaz o una clase abstracta ; sin embargo, la recomendación es no utilizar la herencia en los DTO.

mythz avatar May 25 '2012 18:05 mythz

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

Si lo cambia a lo siguiente, funcionará;

public class Container<T> 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<Dog> { Animal = new Dog() { Name = "dog1" } };
var json = JsonSerializer.SerializeToString<Container<Dog>>(c);
var c2 = JsonSerializer.DeserializeFromString<Container<Dog>>(json);

c.Animal.Speak(); //Works
c2.Animal.Speak(); 
daryal avatar May 25 '2012 08:05 daryal