使用派生类在 C# 中进行序列化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/754850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:55:26  来源:igfitidea点击:

Serialization in C# with a derived class

c#serializationderived-class

提问by srodriguez

I'm building a notification framework and for that I'm serializing and deserializing a basic class, from which all the classes I want to send will derive.

我正在构建一个通知框架,为此我正在序列化和反序列化一个基本类,我想发送的所有类都将从该类派生。

The problem is that the code compiles, but when I actually try to serialize this basic class I get an error saying

问题是代码可以编译,但是当我实际尝试序列化这个基本类时,我收到一条错误消息

System.Runtime.Serialization.SerializationException: Type 'Xxx.DataContracts.WQAllocationUpdate' in Assembly 'Xxx.DataContract, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

System.Runtime.Serialization.SerializationException:程序集“Xxx.DataContract,版本=1.0.0.0,文化=中性,PublicKeyToken=null”中的类型“Xxx.DataContracts.WQAllocationUpdate”未标记为可序列化。

Here is the code :

这是代码:

public class WCallUpdate : NotificationData
{
    private string m_from = "";
    [DataMember]
    public string From
    {
        get { return m_from; }
        set { m_from = value; }
    }
    private WCall m_wCall = new WCall();
    [DataMember]
    public WCall Call
    {
        get { return m_wCall; }
        set { m_wCall = value; }
    }
}

The DataContractfor the Notification is:

DataContract对通知如下:

/// <summary>
/// Basic class used in the notification service
/// </summary>
[DataContract]
public class NotificationData
{
}

/// <summary>
/// Enum containing all the events used in the application
/// </summary>
[DataContract]
public enum NotificationTypeKey
{
    [EnumMember]
    Default = 0,
    [EnumMember]
    IWorkQueueServiceAttributionAddedEvent = 1,
    [EnumMember]
    IWorkQueueServiceAttributionUpdatedEvent = 2,
    [EnumMember]
    IWorkQueueServiceAttributionRemovedEvent = 3,
}

The code used to serialize the data is:

用于序列化数据的代码是:

    #region Create Message
    /// <summary>
    /// Creates a memoryStream from a notificationData
    /// note: we insert also the notificationTypeKey at the beginning of the
    /// stream in order to treat the memoryStream correctly on the client side
    /// </summary>
    /// <param name="notificationTypeKey"></param>
    /// <param name="notificationData"></param>
    /// <returns></returns>
    public MemoryStream CreateMessage(NotificationTypeKey notificationTypeKey, NotificationData notificationData)
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        try
        {
            formatter.Serialize(stream, notificationTypeKey);
            formatter.Serialize(stream, notificationData);
        }
        catch (Exception ex)
        {
            Logger.Exception(ex);
        }
        return stream;
    }
    #endregion

When I try to create a message:

当我尝试创建消息时:

WCallUpdate  m_wCallUpdate = new WCallUpdate();
NotificationTypeKey  m_notificationTypeKey = new NotificationTypeKey.Default;
CreateMessage(notificationTypeKey , wCallUpdate );

I got the following error:

我收到以下错误:

System.Runtime.Serialization.SerializationException: Type 'Xxx.DataContracts.WCall' in Assembly 'Xxx.DataContract, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
   at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
   at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   at Xxx.Notification.NotificationMessageFactory.CreateMessage(NotificationTypeKey notificationTypeKey, NotificationData notificationData) in Xxx.Notification\NotificationCenter.cs:line 36

If I put the Serializable flag before the DataContractone does not solve the problem.

如果我在 Serializable 标志之前放置DataContract一个并不能解决问题。



thank you for the fast answer. Sorry that i forgot to put the code of the NotificationData (edited in the main post)

谢谢你的快速答复。对不起,我忘了把 NotificationData 的代码(在主帖中编辑)

I tried putting the Serializable attribute to both class without success :(

我尝试将 Serializable 属性放到两个类中,但没有成功:(

#region NotificationData
/// <summary>
/// Basic class used in the notification service
/// </summary>
[Serializable]
[DataContract]
public class NotificationData
{
}
#endregion

and

[Serializable]
public class WCallUpdate : NotificationData
{
    private string m_from = "";
    [DataMember]
    public string From
    {
        get { return m_from; }
        set { m_from = value; }
    }
    private WCall m_wCall = new WCall();
    [DataMember]
    public WCall Call
    {
        get { return m_wCall; }
        set { m_wCall = value; }
    }
}

**Edit: ** Mea culpa afterall :) You were both right. I forgot to spread the [Serializable]Attribute to all the child class. After updating and compiling, i got no longer the exception. thank you both for your correct answers :)

** 编辑:** 毕竟是我的过错 :) 你们都是对的。我忘了将[Serializable]属性传播到所有子类。更新和编译后,我不再出现异常。谢谢你们的正确答案:)



@Marc Gravel: Actually i thought about what you are suggesting, and created the following DataContractSerializer, but I'm not sure this will work? As my classes use other classes? the big problem with the DataContractSerializer is that you need to specify the type of the object you want to serialize, and as my class uses other class as private fields, that might cause a problem right?

@Marc Gravel:实际上我考虑过您的建议,并创建了以下 DataContractSerializer,但我不确定这是否可行?因为我的班级使用其他班级?DataContractSerializer 的一个大问题是您需要指定要序列化的对象的类型,并且由于我的类使用其他类作为私有字段,这可能会导致问题,对吗?

#region DataContractSerializer
        /// <summary>
        /// Creates a Data Contract Serializer for the provided type. The type must be marked with
        /// the data contract attribute to be serialized successfully.
        /// </summary>
        /// <typeparam name="T">The type to be serialized</typeparam>
        /// <returns>A data contract serializer</returns>
        public static DataContractSerializer CreateDataContractSerializer<T>() where T : class
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(T));
            return serializer;
        }
        #endregion

采纳答案by Darren Kopp

put [Serializable] at the top of the class. Serializable isn't necessarily inherited either AFAIK. meaning even if the base class has [Serializable], you still need it on the descendent class.

将 [Serializable] 放在类的顶部。序列化不一定继承AFAIK。这意味着即使基类具有 [Serializable],您仍然需要在后代类上使用它。

回答by Patrick Peters

To get a class to be serializable mark it with the serializable attribute or derived it from MarshalByRefObject.

要使类可序列化,请使用可序列化属性标记它或从 MarshalByRefObject 派生它。

You derive from NotificationData, is it serializable then?

你从 NotificationData 派生,它是可序列化的吗?

Also check this: when serializable data classes are put in an assembly check your project or file reference in Visual Studio to be sure you get the right one.

还要检查这一点:将可序列化的数据类放入程序集中时,请检查 Visual Studio 中的项目或文件引用,以确保获得正确的引用。

Also if you sign the assembly and put it in the GAC, be sure that the assembly in the GAC is the right one! I have encountered many time consuming debugsessions because I updated the assembly from version 1.0.0.0 to 1.0.0.1 and forgot to replace the old one in the GAC. Assemblies in the GAC are loaded prior the local assemblies, keep that in mind. And... binary formatting is very strict related to assembly versions.

此外,如果您签署程序集并将其放入 GAC,请确保 GAC 中的程序集是正确的!我遇到了很多耗时的调试会话,因为我将程序集从版本 1.0.0.0 更新到 1.0.0.1 并且忘记替换 GAC 中的旧版本。GAC 中的程序集在本地程序集之前加载,请记住这一点。而且...二进制格式与程序集版本的相关性非常严格。

回答by Marc Gravell

I'm very confused why you're using BinaryFormatterwith a data-contract. It would be normal to use DataContractSerializerhere... the logic is then similar to using [Serializable], except you need [DataContract], and it serializes the nominated ([DataMember]) members, rather than the fields which BinaryFormatterworks with.

我很困惑你为什么要使用BinaryFormatter数据合同。DataContractSerializer在这里使用是正常的...逻辑类似于 using [Serializable],除了您需要[DataContract],并且它序列化指定的 ( [DataMember]) 成员,而不是使用的字段BinaryFormatter

Actually, for numerous reasons (such as brittleness) I would suggest switching to DataContractSerializer, especially as that seems to be your intention. Or if you want a more compact binary form, protobuf-netmay be useful (plus is portable between platforms, too).

实际上,出于多种原因(例如脆性),我建议改用DataContractSerializer,尤其是因为这似乎是您的意图。或者,如果您想要更紧凑的二进制形式,protobuf-net可能会很有用(此外还可以在平台之间移植)。

As an aside - you don't need the [DataContract]on enums - it does no harm, but doesn't do a lot either.

顺便说一句- 你不需要[DataContract]on enums - 它没有害处,但也没有太大作用。

回答by David

I have created a class XList to accomplish this:

我创建了一个类 XList 来完成这个:

AA D1=new AA(); //Derived type
BB D2=new BB(); //Derived type 
CC D3=new CC(); //Derived type 
X D4=new X(); //Base Class 

XList<X> AllData=new XList<X>(); 
AllData.Add(D1); 
AllData.Add(D2); 
AllData.Add(D3); 
AllData.Add(D4); 
// ----------------------------------- 
AllData.Save(@"C:\Temp\Demo.xml"); 
// ----------------------------------- 
// Retrieve data from XML file 
// ----------------------------------- 
XList<X> AllData=new XList<X>(); 
AllData.Open(@"C:\Temp\Demo.xml"); 
// -----------------------------------

More details can be found here.

可以在此处找到更多详细信息。