4

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

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

System.Runtime.Serialization.SerializationException:在程序集“Xxx.DataContract,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”中键入“Xxx.DataContracts.WQAallocationUpdate”未标记为可序列化。

这是代码:

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

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

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

    #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

当我尝试创建消息时:

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

我收到以下错误:

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

如果我把 Serializable 标志放在前面DataContract并不能解决问题。


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

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

#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 :)


@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?

#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
4

4 回答 4

20

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.

于 2009-04-16T05:53:40.137 回答
6

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

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-net may be useful (plus is portable between platforms, too).

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

于 2009-04-16T07:10:56.677 回答
2

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

You derive from NotificationData, is it serializable then?

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.

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.

于 2009-04-16T05:38:51.313 回答
0

I have created a class XList to accomplish this:

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.

于 2011-03-08T06:51:54.247 回答