反序列化器不知道映射到此合同的任何类型

时间:2009-04-10 06:44:48

标签: c# serialization datacontractserializer

我正在尝试序列化和反序列化Node对象树。我的抽象“Node”类以及从中派生的其他抽象和具体类在我的“Informa”项目中定义。另外,我在Informa中创建了一个静态类,用于序列化/反序列化。

首先,我将我的树解构为类型字典(guid,Node)的平面列表,其中guid是Node的唯一ID。

我能够在没有问题的情况下序列化我的所有节点。但是当我尝试反序列化时,我得到以下异常。

  

第1行位置错误227.元素   'http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value'   包含的数据   'Informa:建立'数据合同。该   deserializer没有任何知识   映射到此合同的类型。加   与“建筑”对应的类型   到已知类型列表 - for   例如,通过使用   KnownTypeAttribute或将其添加到   传递给的已知类型列表   DataContract Serializer。

从Node派生的所有类(包括Building)都应用 [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;
    }

}

3 个答案:

答案 0 :(得分:3)

  

所有派生自Node的类,   包括建筑,有   [KnownType(typeof(type t))]属性   适用于他们。

KnownType通常应用于 base 类型 - 即。

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

(注意 - 你也可以在DataContractSerializer构造函数中指定已知类型,而不需要属性)

编辑您的回复

如果framwork类不知道所有派生类型,那么在创建序列化器时需要指定已知类型:

[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,可以将其与(例如)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);
            }
        }
    }
}

答案 1 :(得分:1)

好的,这是一个应该让事情更清晰的图表。我正在为另一个程序开发一个插件,它添加了程序中尚未包含的关系和属性。这些关系/属性在我的树结构中定义。但是,我试图抽象地定义这个结构,以便我可以为不同的程序创建插件的实现,以及在单个“查看器”程序中访问来自多个实现的信息。

我的Serialize / Deserialize方法在框架中定义,但框架并不了解所有实现。我希望我可以避免让实现项目将类型列表传递给框架项目中的save / open / serialize / deserialize方法,但似乎我无法避免这种情况?我想这是有意义的,序列化/反序列化方法必须能够访问它们反序列化的类型。

http://dl.getdropbox.com/u/113068/informa_framework.jpg&lt; ---更大的版本 alt text http://dl.getdropbox.com/u/113068/informa_framework.jpg

所以这并没有真正解释Building的问题,因为它是框架项目中的具体类。我认为当我序列化DataContractSerializer可以访问所有对象及其KnowType参数并正确保存时,会发生什么。但是当我反序列化时,我使用Dictionary作为类型创建我的DataContractSerializer。所以它只知道节点,但不知道派生的节点类。

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

我无法告诉它所有派生类型是什么,因为我说它们在框架项目不知道的其他项目中。 Soooooooo似乎是最好的解决方案,让每个实现都将它使用的Node类型列表传递给框架项目中的序列化和反序列化方法。

这有意义吗?有更好的方法吗?

答案 2 :(得分:0)

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;}
}