2012-05-25 9 views
18

私はServiceStackを使用して、いくつかのオブジェクトをJSONにシリアル化および逆シリアル化しています。この例を考えてみましょう:動物のフィールドは動物の種類ではなく、犬の種類としてインスタンス化されているためタイプ情報を保持するためのServiceStackの取得

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がスローされます。この特定のインスタンスがDog型の情報を保持するようにServiceStackに指示する方法はありますか?

答えて

35

継承は悪い考えです - DTOのは、自己記述可能な限り可能と継承クライアントを使用することによって効果的にサービスが最終的に返す何見当がつかないなければなりません。そのため、あなたのDTOクラスは、ほとんどの「標準ベースの」シリアライザで適切にシリアル化/シリアル化できません。

DTOにインターフェイスを持たせる理由はまったくありません(また、POCOモデルで使用する理由もほとんどありません)。インターフェイスを使用してアプリケーションコードのカップリングを減らして、意図しないDTOに漏れてしまいます。しかし、プロセス境界を越えて、インタフェースはシリアル化の具体的な実装のヒントを出さなければならないので、コンシューマはどのような具体的な型を逆シリアル化するのか分からないため、結合を追加するだけです(コードが減少します)。 C#ネームスペースはシリアル化を中断します)、特定のシリアライザで使用するレスポンスを制限します。ワイヤに関するC#の関心が漏れていると、相互運用性を実現するためのサービスのコア目標の1つに違反しています。

JSON仕様の「型情報」の概念がないため、継承はJSONシリアライザで働くようにするために、彼らはに必要な、このタイプの情報含めて独自の拡張機能にJSON wireformatからを発する - 今のカップルあなたのJSONをペイロードを特定のJSONシリアライザ実装に追加します。

ServiceStack's JsonSerializer__typeプロパティで、それはかなりのペイロードを膨らませることができるので、この種の情報は、唯一それを必要とするタイプ、すなわちInterfaces、遅延バインディングobject種類やabstractクラスのため、この種の情報を出力します。それと

は、勧告がしかしのDTOに継承を使用しないソリューションはインタフェースまたは抽象クラスでどちらかにAnimalを変更するだろうと述べました。

+0

私は本質的に同じDTOを取る複数のサービスを持っている場合に何をすべきですか?たとえば、ユーザー登録サービスとユーザー保守サービスはありますか?どちらのフィールドにもフィールドを入力する必要のあるUserAccount DTOがあります。リポジトリは、UserAccountテーブルを挿入または更新する予定であるため、UserAccount DTOが必要です。そこで私はUserAccount DTOを作成しました。 UserRegistration DTOとUserMaintenance DTOは、それを継承します。 [ServiceStackの作成に大きな感謝を寄せています。] – GeorgeBarker

+4

ええと...自分の答えは明らかに明白なコメント/質問です。メッセージはビジネスドメインオブジェクトになるべきではなく、もっと)。 "持っている"と "ある"の単純な問題は "です"。したがって、私の例では、UpdateUserMsgとRegisterUserMsgはどちらもUserAccount DTOを含んでいます。どちらもUserAccount DTOではありません。継承の必要はありません。 – GeorgeBarker

+0

継承のないリストはどのように扱いますか?例えば、 'List 'というプロパティを持つDTOを持っていて、 'IAnimal'を実装する20のクラスがあります。 – Cocowalla

1

シリアル化されたオブジェクトがdogかどうかにかかわらず、動物オブジェクトのプロパティのみをシリアル化しています。 "Name"のような犬のクラスにパブリックプロパティを追加しても、シリアル化されないので、逆シリアル化するときには "Animal"クラスのプロパティしか持たないでしょう。

次のように変更すると動作します。 DTOで

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

ありがとうございます。私はこの問題を回避するためにジェネリックスを使用することはできません - 私は基本的に'List 'を持っています。各コンテナには異なるタイプの'アニマル 'があります。私は、これを回避するための設定やトリックがあることを期待していましたが、この機能を持たずに生きなければならないと思います。 – larspars

1

はたぶんオフトピックですが、Newtonsoftシリアライザは、オプションを含むことを行うことができます。

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

これは、オブジェクトの強いタイプで$型と呼ばれるJSON内のプロパティを作成します。デシリアライザを呼び出すと、その値が同じタイプのオブジェクトを再構築するために使用されます。次のテストは、ServiceStackではなく、強いタイプのnewtonsoftを使用して動作します。

[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; } 
} 
関連する問題