获取ServiceStack以保留类型信息

时间:2012-05-25 07:50:25

标签: c# .net json serialization 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<Container>(json);

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

最后一行抛出InvalidCastException,因为Animal字段被实例化为Animal类型,而不是Dog类型。有什么办法可以告诉ServiceStack保留这个特定实例属于Dog类型的信息吗?

3 个答案:

答案 0 :(得分:35)

DTO中的继承是一个坏主意 - DTO应该尽可能地自我描述,并且通过使用继承客户端实际上不知道服务最终返回什么。这就是为什么你的DTO类在大多数“基于标准的”序列化器中无法正确/序列化的原因。

没有充分的理由在DTO中使用接口(并且很少有理由将它们放在POCO模型上),这是一种使用接口来减少应用程序代码中的耦合的商业习惯,这些代码被无意中泄漏到DTO中。但是跨进程边界,接口只增加了耦合(它只在代码中减少),因为消费者不知道要反序列化的具体类型,所以它必须发出序列化特定的实现提示,现在将C#问题嵌入到线上(所以现在甚至C#命名空间将破坏序列化)现在约束您的响应将被特定的序列化程序使用。在线路上泄漏C#问题违反了实现互操作性的服务的核心目标之一。

由于JSON规范中没有'type info'概念,为了继承在JSON Serializers中工作,他们需要发布专有扩展JSON wireformat以包含此内容type info - 现在将您的JSON有效负载耦合到特定的JSON序列化器实现。

ServiceStack's JsonSerializer将此类型信息存储在 __ type 属性中,并且由于它可以显着增加有效负载,因此只会为需要它的类型发出此类型信息,即Interfaces ,后期绑定object类型或abstract类。

据说,解决方案是将Animal更改为接口抽象类,但建议不要使用继承DTO的。

答案 1 :(得分:1)

您只序列化动物对象的属性,无论序列化对象是否为狗。即使你向dog类添加一个公共属性,比如“Name”,它也不会被序列化,因此当你反序列化时,你只会拥有“Animal”类的属性。

如果将其更改为以下内容,则可以使用;

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

答案 2 :(得分:1)

可能是偏离主题但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<FakeA>();
        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; }
}
相关问题