Сериализация в C # с производным классом - PullRequest
4 голосов
/ 16 апреля 2009

Я создаю инфраструктуру уведомлений, и для этого я сериализую и десериализую базовый класс, из которого будут получены все классы, которые я хочу отправить.

Проблема в том, что код компилируется, но когда я на самом деле пытаюсь сериализовать этот базовый класс, я получаю сообщение об ошибке

System.Runtime.Serialization.SerializationException: Тип 'Xxx.DataContracts.WQAllocationUpdate' в сборке 'Xxx.DataContract, Version = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null' не помечен как сериализуемый.

Вот код:

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

Если я поставлю флаг Сериализуемый перед DataContract, это не решит проблему.


спасибо за быстрый ответ. Извините, что забыл поставить код NotificationData (отредактировано в основном посте)

Я попытался поместить атрибут Serializable в оба класса без успеха: (

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

и

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

** Редактировать: ** Mea culpa afterall :) Вы оба были правы. Я забыл распространить атрибут [Serializable] на все дочерние классы. После обновления и компиляции я больше не получил исключение. спасибо вам обоим за правильные ответы:)


@ Марк Гравий: На самом деле я подумал о том, что вы предлагаете, и создал следующий 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

Ответы [ 4 ]

19 голосов
/ 16 апреля 2009

поставить [Сериализуемый] на вершину класса. Сериализуемый не обязательно наследуется либо AFAIK. это означает, что даже если базовый класс имеет [Serializable], он все еще нужен в классе потомков.

6 голосов
/ 16 апреля 2009

Я очень смущен, почему вы используете BinaryFormatter с контрактом данных. Было бы нормально использовать DataContractSerializer здесь ... логика тогда аналогична использованию [Serializable], за исключением того, что вам нужно [DataContract], и она сериализует назначенные ([DataMember]) члены, а не поля, которые BinaryFormatter работает с.

На самом деле, по многим причинам (, таким как хрупкость ), я бы предложил переключиться на DataContractSerializer, тем более что это, по-видимому, является вашим намерением. Или, если вам нужна более компактная двоичная форма, protobuf-net может быть полезен (плюс также переносим между платформами).

В качестве отступления - вам не нужны [DataContract] на enum с - это не причиняет вреда, но и не приносит большой пользы.

2 голосов
/ 16 апреля 2009

Чтобы класс был сериализуемым, пометьте его атрибутом serializable или наследуйте от MarshalByRefObject.

Вы производите от NotificationData, это сериализуемо тогда?

Также проверьте это: когда сериализуемые классы данных помещаются в сборку, проверьте ваш проект или ссылку на файл в Visual Studio, чтобы убедиться, что вы получите правильный.

Также, если вы подписываете сборку и помещаете ее в GAC, убедитесь, что сборка в GAC правильная! Я столкнулся со многими отнимающими много времени отладками, потому что я обновил сборку с версии 1.0.0.0 до 1.0.0.1 и забыл заменить старую в GAC. Сборки в GAC загружаются до локальных сборок, имейте это в виду. И ... двоичное форматирование очень строго связано с версиями сборки.

0 голосов
/ 08 марта 2011

Я создал класс 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"); 
// -----------------------------------

Более подробную информацию можно найти здесь .

...