4

Node オブジェクトのツリーをシリアライズおよびデシリアライズしようとしています。抽象「Node」クラスと、そこから派生する他の抽象クラスおよび具象クラスは、「Informa」プロジェクトで定義されています。さらに、Informa でシリアライゼーション/デシリアライゼーション用の静的クラスを作成しました。

まず、ツリーをDictionary(guid,Node)型のフラット リストに分解します。ここで、guid は Node の一意の ID です。

問題なくすべてのノードをシリアル化できます。しかし、逆シリアル化しようとすると、次の例外が発生します。

行 1 位置 227 のエラー。要素 ' http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value ' には、'Informa:Building' データ コントラクトのデータが含まれています。デシリアライザーには、このコントラクトにマップされる型の知識はありません。'Building' に対応する型を既知の型のリストに追加します。たとえば、KnownTypeAttribute を使用するか、DataContract シリアライザーに渡される既知の型のリストに追加します。

Building を含む Node から派生するすべてのクラスには、[KnownType(typeof(type t))]属性が適用されます。

私のシリアライゼーションとデシリアライゼーションの方法は次のとおりです。

public static void SerializeProject(Project project, string filePath)
{
    try
    {
        Dictionary<Guid, Node> nodeDic = DeconstructProject(project);

        Stream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);

        //serialize

        DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary<Guid, Node>),"InformaProject","Informa");

        ser.WriteObject(stream,nodeDic);

        // Cleanup
        stream.Close();
    }
    catch (Exception e)
    {
        MessageBox.Show("There was a problem serializing " + Path.GetFileName(filePath) + ". \n\nException:" + e.Message, "Doh!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        throw e;
    }

}



public static Project DeSerializeProject(string filePath)
{
    try
    {
        Project proj;

        // Read the file back into a stream
        Stream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

        DataContractSerializer ser = new DataContractSerializer(typeof(Dictionary<Guid, Node>), "InformaProject", "Informa");

        Dictionary<Guid, Node> nodeDic = (Dictionary<Guid, Node>)ser.ReadObject(stream);

        proj = ReconstructProject(nodeDic);        

        // Cleanup
        stream.Close();

        return proj;

    }
    catch (Exception e)
    {
        MessageBox.Show("There was a problem deserializing " + Path.GetFileName(filePath) + ". \n\nException:" + e.Message, "Doh!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return null;
    }

}
4

3 に答える 3

3

Building を含む Node から派生するすべてのクラスには、[KnownType(typeof(type t))] 属性が適用されます。

KnownType通常、ベースタイプに適用されます-つまり

[DataContract, KnownType(typeof(Building)), ...]
abstract class Node { ... }

(注 -DataContractSerializer属性を必要とせずに、コンストラクターで既知の型を指定することもできます)

返信を編集してください

フレームワーク クラスがすべての派生型を認識していない場合は、シリアライザーを作成するときに既知の型を指定する必要があります。

[DataContract] abstract class SomeBase { }
[DataContract] class Foo : SomeBase { }
[DataContract] class Bar : SomeBase { }
...
// here the knownTypes argument is important
new DataContractSerializer(typeof(SomeBase),
      new Type[] { typeof(Foo), typeof(Bar) });

これは、前の例preserveObjectReferencesの を置き換えることで (たとえば) etc と組み合わせることができます。null

編集終了

ただし、再現可能なもの (つまりNodeBuilding) がなければ、あまり役に立ちません。

もう一つの奇妙なこと。ツリー構造は、次のような場合に非常に適しDataContractSerializerています。ツリーは xml で簡単に表現できるため、通常は最初にフラット化する必要はありません。本当に平らにする必要がありますか?


例:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

[DataContract, KnownType(typeof(Building))]
abstract class Node {
    [DataMember]
    public int Foo {get;set;}
}
[DataContract]
class Building : Node {
    [DataMember]
    public string Bar {get;set;}
}

static class Program
{
    static void Main()
    {
        Dictionary<Guid, Node> data = new Dictionary<Guid, Node>();
        Type type = typeof(Dictionary<Guid, Node>);
        data.Add(Guid.NewGuid(), new Building { Foo = 1, Bar = "a" });
        StringWriter sw = new StringWriter();
        using (XmlWriter xw = XmlWriter.Create(sw))
        {
            DataContractSerializer dcs = new DataContractSerializer(type);
            dcs.WriteObject(xw, data);
        }

        string xml = sw.ToString();

        StringReader sr = new StringReader(xml);
        using (XmlReader xr = XmlReader.Create(sr))
        {
            DataContractSerializer dcs = new DataContractSerializer(type);
            Dictionary<Guid, Node> clone = (Dictionary<Guid, Node>)
                dcs.ReadObject(xr);
            foreach (KeyValuePair<Guid, Node> pair in clone)
            {
                Console.WriteLine(pair.Key + ": " + pair.Value.Foo + "/" +
                    ((Building)pair.Value).Bar);
            }
        }
    }
}
于 2009-04-10T07:29:44.217 に答える
1

わかりました、これが物事をより明確にする図です。プログラムにまだ含まれていない関係とプロパティを追加する別のプログラムのプラグインを開発しています。これらの関係/プロパティは、ツリー構造で定義されています。ただし、この構造を抽象的に定義して、さまざまなプログラムのプラグインの実装を作成し、単一の「ビューアー」プログラムで複数の実装からの情報にアクセスできるようにしようとしています。

Serialize/Deserialize メソッドはフレームワークで定義されていますが、フレームワークはすべての実装を認識していません。実装プロジェクトが型のリストをフレームワーク プロジェクトの save/open/serialize/deserialize メソッドに渡すことを回避できることを望んでいましたが、これを回避できないようですか? それは理にかなっていると思いますが、シリアル化/逆シリアル化メソッドは、逆シリアル化する型にアクセスできる必要があります。

http://dl.getdropbox.com/u/113068/informa_framework.jpg <---大きいバージョン の代替テキスト http://dl.getdropbox.com/u/113068/informa_framework.jpg

したがって、Building はフレームワーク プロジェクトの具象クラスであるため、Building の問題を実際に説明することはできません。DataContractSerializer をシリアル化すると、すべてのオブジェクトとその KnowType パラメーターにアクセスして、それらを正しく保存すると、何が起こっていると思いますか。しかし、デシリアライズするときは、Dictionary をタイプとして DataContractSerializer を作成します。したがって、ノードについてのみ認識し、ノードの派生クラスについては認識しません。

new DataContractSerializer(typeof(Dictionary<Guid, Node>))

フレームワークプロジェクトが知らない他のプロジェクトにあると言ったように、すべての派生型が何であるかはわかりません。Soooooooo 最良の解決策は、フレームワーク プロジェクトのシリアル化および逆シリアル化メソッドに使用する Node タイプのリストを各実装に渡すことです。

これは理にかなっていますか?これを行うより良い方法はありますか?

于 2009-04-10T20:49:01.237 に答える
0

これら 2 つの KnownTypes 属性の使用方法の違いは何ですか? あるクラスが別のクラスの KnownType であることを指定できる/したいということに気づきませんでした。

[DataContract]
[KnownType(typeof(Building))]
abstract class Node {
    [DataMember]
    public int Foo {get;set;}
}
[DataContract]
class Building : Node {
    [DataMember]
    public string Bar {get;set;}
}

[DataContract] 
[KnownType(typeof(Node))]
abstract class Node {
    [DataMember]
    public int Foo {get;set;}
}
[KnownType(typeof(Building))]
class Building : Node {
    [DataMember]
    public string Bar {get;set;}
}
于 2009-04-10T09:48:07.580 に答える