【问题标题】:The deserializer has no knowlege of any type that maps to this contract反序列化器不知道映射到该合约的任何类型
【发布时间】:2010-10-18 17:23:18
【问题描述】:

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

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

我能够毫无问题地序列化我的所有节点。但是当我尝试反序列化时,会出现以下异常。

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

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

}

【问题讨论】:

标签: c# serialization datacontractserializer


【解决方案1】:

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

KnownType 通常应用于 base 类型 - 即

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

这可以通过替换上一个示例中的null 与(例如)preserveObjectReferences 等结合使用。

结束编辑

但是,如果没有可重现的东西(即 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);
            }
        }
    }
}

【讨论】:

  • 我的树结构允许节点有多个父节点。这使序列化复杂化,因为我不想创建重复的实例。
  • 我正在尝试定义有关我的树结构的信息以及在一个“框架”项目中对其进行序列化的方法,然后在其他项目中对其进行实际实现。
  • 所以我的框架项目并不知道所有可能从 Node.js 派生的类型。但是 Building 是在我的框架项目中,所以我认为这还不是问题。
【解决方案2】:

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

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

http://dl.getdropbox.com/u/113068/informa_framework.jpg alt text http://dl.getdropbox.com/u/113068/informa_framework.jpg

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

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

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

这有意义吗?有没有更好的方法来做到这一点?

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • No... [KnownType] 与继承有关;序列化程序需要知道在处理 Node.js 时它应该期待 Building 实例(以及其他实例)。我将编辑我的答案以澄清。
    猜你喜欢
    • 1970-01-01
    • 2011-08-31
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 2017-01-19
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    相关资源
    最近更新 更多