反序列化器不了解映射到此合约的任何类型

发布于 2024-07-16 11:34:26 字数 2405 浏览 3 评论 0原文

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

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

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

第 1 行位置 227 处出现错误。元素 'http://schemas.microsoft.com/2003/10/Serialization/Arrays :值' 包含的数据 “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;
    }

}

I'm trying to serialize and deserialize a tree of Node objects. My abstract "Node" class as well as other abstract and concrete classes that derive from it are defined in my "Informa" project. In addition, I've created a static class in Informa for serialization / deserialization.

First I'm deconstructing my tree into a flat list of type Dictionary(guid,Node) where guid is the unique id of Node.

I am able to serialize all my Nodes with out a problem. But when I try to deserialize I get the following exception.

Error in line 1 position 227. Element
'http://schemas.microsoft.com/2003/10/Serialization/Arrays:Value'
contains data of the
'Informa:Building' data contract. The
deserializer has no knowlege of any
type that maps to this contract. Add
the type corresponding to 'Building'
to the list of known types - for
example, by usying the
KnownTypeAttribute or by adding it to
the list of known types passed to
DataContract Serializer.

All classes that derive from Node, including Building, have the [KnownType(typeof(type t))] attribute applied to them.

My serialization and deserialization methods are below:

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

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凑诗 2024-07-23 11:34:26

所有从 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) });

这可以与(例如)preserveObjectReferences 等,替换前面示例中的 null

END EDIT

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

All classes that derive from Node,
including Building, have the
[KnownType(typeof(type t))] attribute
applied to them.

KnownType is usually applied to the base type - i.e.

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

(note - you can also specify the known-types in the DataContractSerializer constructor, without requiring attributes)

EDIT RE YOUR REPLY

If the framwork class doesn't know about all the derived types, then you need to specify the known types when creating the serializer:

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

This can be combined with (for example) preserveObjectReferences etc by replacing the null in the previous example.

END EDIT

However, without something reproducible (i.e. Node and Building), it is going to be hard to help much.

The other odd thing; trees structures are very well suited to things like DataContractSerializer - there is usually no need to flatten them first, since trees can be trivially expressed in xml. Do you really need to flatten it?


Example:

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);
            }
        }
    }
}
节枝 2024-07-23 11:34:26

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

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

http://dl.getdropbox.com/u/113068/informa_framework.jpg <---大版本
替代文本 http://dl.getdropbox.com/u/113068/informa_framework.jpg< /a>

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

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

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

这有道理吗? 有一个更好的方法吗?

Alright here's a diagram that should make things more clear. I'm developing a plugin for another program that adds relationships and properties not already included in the program. These relationships/properties are defined in my tree structure. However, I'm trying to define this structure abstractly so that I could create implementations of the plugin for different programs, as well as access the information from multiple implementations in a single "viewer" program.

My Serialize/Deserialize methods are defined in the framework, but the framework does not know about all the implementations. I was hoping I could avoid having the implementation projects pass a list of types to the save/open/serialize/deserialize methods in the framework project, but it seems I can't avoid this? I guess that make sense though, the serialize/deserialize methods must have access to the types they are deserializing.

http://dl.getdropbox.com/u/113068/informa_framework.jpg <---Larger Version
alt text http://dl.getdropbox.com/u/113068/informa_framework.jpg

So this doesn't really explain the problem with Building, as it is a concrete class in the framework project. I think what is happening is when I serialize the DataContractSerializer has access to all the objects and their KnowType parameter and saves them correctly. But when I deserialize I create my DataContractSerializer with Dictionary as the type. So it only knows about Nodes, but not the derived classes of nodes.

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

I can't tell it what all the derived types are because like I said they are in other projects that the framework project doesn't know about. Soooooooo seems like the best solution is to have each implementation pass a list of Node types it uses to the serialization and deserialization methods in the framework project.

Does this make sense? Is there a better way to do this?

旧城烟雨 2024-07-23 11:34:26

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

What is the difference between these two uses of the KnownTypes attribute? I didn't realize that you could/would want to specify that one class is a KnownType of another.

[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;}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文