Obtendo ServiceStack para reter informações de tipo

Estou usando o ServiceStack para serializar e desserializar alguns objects para JSON. Considere este exemplo:

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 

A última linha lança uma InvalidCastException, porque o campo Animal é instanciado como um tipo Animal, não um tipo Cachorro. Existe alguma maneira que eu possa dizer ao ServiceStack para reter as informações que essa instância específica era do tipo Dog?

A inheritance em DTOs é uma má idéia – os DTOs devem ser tão autodescritivos quanto possível e, ao usar clientes de inheritance, efetivamente não fazem idéia do que o serviço finalmente retorna. É por isso que suas classs DTO não serão serializadas adequadamente na maioria dos serializadores baseados em padrões.

Não há uma boa razão para ter interfaces em DTOs (e muito poucas razões para tê-las em modelos POCO), é um hábito de carga de usar interfaces para reduzir o acoplamento no código do aplicativo que está sendo inutilmente vazado em DTOs. Mas, além dos limites do processo, as interfaces apenas adicionam acoplamento (ele é reduzido apenas no código), pois o consumidor não tem ideia do tipo concreto a ser desserializado, de modo que ele precisa emitir dicas de implementação específicas da serialização que agora incorporam preocupações de C # no fio C # namespaces irão quebrar a serialização) e agora restringe sua resposta para ser usada por um serializador em particular. Vazamento preocupações C # sobre o fio viola um dos principais objectives dos serviços para permitir a interoperabilidade.

Como não há nenhum conceito de ‘type info’ na especificação JSON, para que a inheritance funcione em JSON Serializers, eles precisam emitir extensões proprietárias para o formato wire do JSON para include este tipo de informação – que agora une sua carga JSON a um JSON específico. implementação de serializador.

O JsonSerializer do ServiceStack armazena essas informações de tipo na propriedade __type e, como pode aumentar consideravelmente a carga útil, emitirá apenas essas informações de tipo para os tipos que precisam, ou seja, Interfaces , tipos de object binding tardia ou classs abstract .

Com isso dito, a solução seria alterar Animal para ser uma Interface ou uma class abstrata , mas a recomendação é não usar inheritance em DTOs.

Você está serializando apenas as propriedades do object animal, se o object serializado é cão ou não. Mesmo se você adicionar uma propriedade pública à class dog, como “Name”, ela não será serializada, então quando você desserializar, você terá apenas propriedades de uma class “Animal”.

Se você mudar para o seguinte, vai 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(); 

Talvez seja off-topic, mas o serializador Newtonsoft pode fazer isso, incluindo a opção:

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

Ele irá criar uma propriedade dentro do json chamada $ type com o tipo forte do object. Quando você chama o desserializador, esse valor será usado para construir o object novamente com os mesmos tipos. O próximo teste funciona usando newtonsoft com tipo forte, não com 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; } }