Pregunta

Estoy construyendo un marco de notificación y para ello estoy serializando y deseriaizando una clase básica, de la cual derivarán todas las clases que quiero enviar.

El problema es que el código se compila, pero cuando intento serializar esta clase básica aparece un error que dice

System.Runtime.Serialization.SerializationException:Escriba 'Xxx.DataContracts.WQAllocationUpdate' en el ensamblado 'Xxx.DataContract, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' no está marcado como serializable.

Aquí está el código:

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

El DataContract para la Notificación es:

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

El código utilizado para serializar los datos es:

    #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

Cuando intento crear un mensaje:

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

Tuve el siguiente 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

Si pongo el indicador Serializable antes del DataContract uno no resuelve el problema.


gracias por la rápida respuesta.Perdón que olvidé poner el código de NotificationData (editado en la publicación principal)

Intenté poner el atributo Serializable en ambas clases sin éxito :(

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

y

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

**Editar:** Mea culpa después de todo :) Ambos tenían razón.Me olvidé de difundir el [Serializable] Atributo a toda la clase secundaria.Después de actualizar y compilar, ya no tuve la excepción.gracias a ambos por sus respuestas correctas :)


@Marc Grava:En realidad, pensé en lo que estás sugiriendo y creé el siguiente DataContractSerializer, pero no estoy seguro de que funcione.¿Como mis clases usan otras clases?El gran problema con DataContractSerializer es que necesitas especificar el tipo de objeto que deseas serializar, y como mi clase usa otras clases como campos privados, eso podría causar un problema, ¿verdad?

#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
¿Fue útil?

Solución

poner [Serializable] en la parte superior de la clase. Serializable no está necesariamente heredado, ya sea AFAIK. es decir, incluso si la clase base tiene [Serializable], todavía lo necesitan en la clase descendiente.

Otros consejos

Estoy muy confundido por qué estás usando BinaryFormatter con un contrato de datos. Sería normal utilizar DataContractSerializer aquí ... entonces la lógica es similar al uso de [Serializable], excepto que necesita [DataContract], y se serializa los miembros ([DataMember]) designado, en lugar de los campos, que BinaryFormatter trabaja.

En realidad, por numerosas razones ( tales como fragilidad ) me gustaría sugerir el cambio a DataContractSerializer, especialmente en lo que parece ser su intención. O si desea una forma binaria más compacto, protobuf-net puede ser útil ( plus es portable entre plataformas, también).

Como acotación al margen - que no es necesario el [DataContract] en enums -. No hace ningún daño, pero no hace mucho tampoco

Para que una clase sea serializable, márquela con el atributo serializable o derivarla de MarshalByRefObject.

Se deriva de NotificationData, ¿es serializable entonces?

También revisa esto:Cuando las clases de datos serializables se colocan en un ensamblado, verifique la referencia de su proyecto o archivo en Visual Studio para asegurarse de obtener la correcta.

Además, si firma el ensamblaje y lo coloca en el GAC, ¡asegúrese de que el ensamblaje en el GAC sea el correcto!Me he encontrado con muchas sesiones de depuración que consumen mucho tiempo porque actualicé el ensamblado de la versión 1.0.0.0 a 1.0.0.1 y olvidé reemplazar el anterior en el GAC.Las asambleas en el GAC se cargan antes que las asambleas locales, téngalo en cuenta.Y...El formato binario es muy estricto en relación con las versiones ensambladas.

He creado una clase XList de lograr esto:

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"); 
// -----------------------------------

Más detalles se pueden encontrar aquí .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top