21

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

Animal フィールドは Dog 型ではなく Animal 型としてインスタンス化されるため、最後の行は InvalidCastException をスローします。この特定のインスタンスが Dog タイプであったという情報を保持するように ServiceStack に指示する方法はありますか?

4

3 に答える 3

39

DTO の継承は悪い考えです。DTO は可能な限り自己記述的であるべきであり、クライアントは継承を効果的に使用することで、サービスが最終的に何を返すかわかりません。これが、ほとんどの「標準ベース」のシリアライザーで DTO クラスが適切にデシリアライズ/シリアライズできない理由です。

DTO にインターフェイスを含める正当な理由はありません (POCO モデルにインターフェイスを含める理由はほとんどありません)。インターフェイスを使用してアプリケーション コード内のカップリングを減らすことは、軽率に DTO にリークされているカーゴ カルトの習慣です。しかし、プロセスの境界を越えて、インターフェイスはカップリングを追加するだけです (コードで削減されるだけです)。消費者は逆シリアル化する具体的な型がわからないため、シリアル化固有の実装ヒントを発行する必要があります。 C# の名前空間はシリアライゼーションを中断します)、特定のシリアライザーによって使用される応答を制限するようになりました。C# の懸念をネットワーク上に漏らすことは、相互運用性を可能にするというサービスの中心的な目標の 1 つに違反します。

JSON 仕様には「型情報」の概念がないため、継承がJSON シリアライザーで機能するためには、JSON ワイヤーフォーマットに独自の拡張機能を発行して、この型情報を含める必要があります。これにより、JSON ペイロードが特定の JSON に結合されます。シリアライザーの実装。

ServiceStack の JsonSerializerは、この型情報を__typeプロパティに格納します。ペイロードがかなり肥大化する可能性があるため、この型情報を必要とする型、つまりInterfaces遅延バインドobject型またはabstractクラスに対してのみ、この型情報を出力します。

そうは言っても、解決策はインターフェイスまたは抽象Animalクラスのいずれかに変更することですが、DTO で継承を使用しないことをお勧めします。

于 2012-05-25T18:00:00.887 に答える
1

シリアル化されたオブジェクトが犬であるかどうかに関係なく、動物オブジェクトのプロパティのみをシリアル化しています。「名前」などのパブリック プロパティを犬のクラスに追加しても、シリアル化されないため、逆シリアル化すると「動物」クラスのプロパティしかありません。

次のように変更すると機能します。

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(); 
于 2012-05-25T08:21:18.360 に答える
1

トピックから外れているかもしれませんが、Newtonsoftシリアライザーはオプションを含めてそれを行うことができます:

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

オブジェクトの強い型で $type と呼ばれる 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; }
}
于 2016-09-28T15:01:05.560 に答える