2010-01-15 16 views
34

Estoy tratando de serializar una clase más datos de los miembros son objetos anulables, aquí es un ejemplonúmeros de serie a un anulable <DateTime> a XML

[XmlAttribute("AccountExpirationDate")] 
public Nullable<DateTime> AccountExpirationDate 
{ 
    get { return userPrincipal.AccountExpirationDate; } 
    set { userPrincipal.AccountExpirationDate = value; } 
} 

Sin embargo en tiempo de ejecución se produce el error

No se puede serialice el miembro 'AccountExpirationDate' de tipo System.Nullable`1 [System.DateTime]. XmlAttribute/XmlText no se puede usar para codificar tipos complejos.

Sin embargo, he comprobado y Nullable es un SerializableAttribute. ¿Qué estoy haciendo mal?

+0

Supongo total, pero ¿has probado DateTime? en lugar de Nullable ? –

+1

@Terry - ¡son idénticos! –

+0

@Terry, no puedo como DateTime no puede contener nulos y userPrincipal.AccountExpirationDate; puede devolver un nulo –

Respuesta

28

Solo puede serializarlo como XmlElement, no como XmlAttribute, ya que la representación es demasiado compleja para un atributo. Eso es lo que la excepción te está diciendo.

+1

Sin embargo, puede crear su propia implementación de Nullable e implementar los atributos de serialización XML necesarios para ello. –

+0

Sí, podría, estuvo de acuerdo. Pero el compilador no lo trataría exactamente de la misma manera que el 'Nullabele incorporado ', que tiene un tratamiento especial (pensando en cosas como la definición implícita de '=='). Entonces no recomendado. –

+0

@NickLarsen - tampoco podrá generar correctamente el esquema a menos que te vuelvas loco con él. –

41

Si lo que desea es que el trabajo , entonces tal vez:

using System; 
using System.ComponentModel; 
using System.Xml.Serialization; 
public class Account 
{ 
    // your main property; TODO: your version 
    [XmlIgnore] 
    public Nullable<DateTime> AccountExpirationDate {get;set;} 

    // this is a shim property that we use to provide the serialization 
    [XmlAttribute("AccountExpirationDate")] 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
    public DateTime AccountExpirationDateSerialized 
    { 
     get {return AccountExpirationDate.Value;} 
     set {AccountExpirationDate = value;} 
    } 

    // and here we turn serialization of the value on/off per the value 
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
    public bool ShouldSerializeAccountExpirationDateSerialized() 
    { 
     return AccountExpirationDate.HasValue; 
    } 

    // test it... 
    static void Main() 
    { 
     var ser = new XmlSerializer(typeof(Account)); 
     var obj1 = new Account { AccountExpirationDate = DateTime.Today }; 
     ser.Serialize(Console.Out, obj1); 
     Console.WriteLine(); 
     var obj2 = new Account { AccountExpirationDate = null}; 
     ser.Serialize(Console.Out, obj2); 
    } 
} 

Esto sólo incluirá el atributo cuando hay un valor no nulo.

+1

¿El Serializador XML verifica un método 'ShouldSerializeXxxxxxx', o está capturando la excepción lanzada por' AccountExpirationDate.Value' que no se serializa? ¿Como funciona esto? –

+3

@Scott sí, ShouldSerialize * es un patrón utilizado por varias partes del framework y varias bibliotecas de serialización –

+0

¿Qué hay acerca de la deserialización? Si el atributo no está establecido en el XML, la propiedad que admite nulos debe ser nula. –

14

He usado algo como esto muchas veces.

[XmlIgnore] 
public Nullable<DateTime> AccountExpirationDate 
{ 
    get { return userPrincipal.AccountExpirationDate; } 
    set { userPrincipal.AccountExpirationDate = value; } 
} 

/// 
/// <summary>Used for Xml Serialization</summary> 
/// 
[XmlAttribute("AccountExpirationDate")] 
public string AccountExpirationDateString 
{ 
    get 
    { 
     return AccountExpirationDate.HasValue 
      ? AccountExpirationDate.Value.ToString("yyyy/MM/dd HH:mm:ss.fff") 
      : string.Empty; 
    } 
    set 
    { 
     AccountExpirationDate = 
      !string.IsNullOrEmpty(value) 
      ? DateTime.ParseExact(value, "yyyy/MM/dd HH:mm:ss.fff") 
      : null; 
    } 
} 
+1

Creo que debería ser: AccountExpirationDate.Value.ToString ("aaaa/MM/dd HH: mm: ss.fff") – Perhentian

+1

Buena captura. Lo he arreglado – fre0n

2

Define un Serializable que encapsula tu funcionalidad.

Aquí hay y ejemplo.

[XmlAttribute("AccountExpirationDate")] 
public SerDateTime AccountExpirationDate 
{ 
    get { return _SerDateTime ; } 
    set { _SerDateTime = value; } 
} 


/// <summary> 
/// Serialize DateTime Class (<i>yyyy-mm-dd</i>) 
/// </summary> 
public class SerDateTime : IXmlSerializable { 
    /// <summary> 
    /// Default Constructor when time is not avalaible 
    /// </summary> 
    public SerDateTime() { } 
    /// <summary> 
    /// Default Constructor when time is avalaible 
    /// </summary> 
    /// <param name="pDateTime"></param> 
    public SerDateTime(DateTime pDateTime) { 
     DateTimeValue = pDateTime; 
    } 

    private DateTime? _DateTimeValue; 
    /// <summary> 
    /// Value 
    /// </summary> 
    public DateTime? DateTimeValue { 
     get { return _DateTimeValue; } 
     set { _DateTimeValue = value; } 
    } 

    // Xml Serialization Infrastructure 
    void IXmlSerializable.WriteXml(XmlWriter writer) { 
     if (DateTimeValue == null) { 
      writer.WriteString(String.Empty); 
     } else { 
      writer.WriteString(DateTimeValue.Value.ToString("yyyy-MM-dd")); 
      //writer.WriteString(SerializeObject.SerializeInternal(DateTimeValue.Value)); 
     } 
    } 

    void IXmlSerializable.ReadXml(XmlReader reader) { 
     reader.ReadStartElement(); 
     String ltValue = reader.ReadString(); 
     reader.ReadEndElement(); 
     if (ltValue.Length == 0) { 
      DateTimeValue = null; 
     } else {     
      //Solo se admite yyyyMMdd 
      //DateTimeValue = (DateTime)SerializeObject.Deserialize(typeof(DateTime), ltValue); 
      DateTimeValue = new DateTime(Int32.Parse(ltValue.Substring(0, 4)), 
           Int32.Parse(ltValue.Substring(5, 2)), 
           Int32.Parse(ltValue.Substring(8, 2)));          
     } 
    } 

    XmlSchema IXmlSerializable.GetSchema() { 
     return (null); 
    } 
} 
#endregion 
2

Me quedé atrapado en el problema similar. Tenía una propiedad datetime (como XmlAttribute) en una clase que se expuso en el servicio WCF.

continuación es lo que me enfrentaba y la solución que trabajó para mí: clase 1) XmlSerializer no se serialising XmlAttribute de anulable tipo

[XmlAttribute] 
public DateTime? lastUpdatedDate { get; set; } 
Exception thrown : Cannot serialize member 'XXX' of type System.Nullable`1. 

2) Algunos mensajes sugieren reemplazar [XmlAttribute] con [XmlElement (IsNullable = verdadero)]. Pero esto serializará el Atributo como un Elemento que es totalmente inútil. Sin embargo, funciona bien para XmlElements

3) Algunos sugieren implementar la interfaz IXmlSerializable en su clase, pero eso no permite que se llame al servicio WCF desde la aplicación que consume WCF. Esto tampoco funciona en este caso.

Solución:

No marque propiedad como anulable, en lugar de utilizar un método ShouldSerializeXXX() para poner su restricción.

[XmlAttribute] 
public DateTime lastUpdatedDate { get; set; } 
public bool ShouldSerializelastUpdatedDate() 
{ 
    return this.lastUpdatedDate != DateTime.MinValue; 
    // This prevents serializing the field when it has value 1/1/0001  12:00:00 AM 
} 
+0

Noté que ha publicado ** exactamente ** la misma respuesta [aquí] (http://stackoverflow.com/a/29936620/1364007), [aquí] (http://stackoverflow.com/a/29936469/1364007) y [aquí] (http://stackoverflow.com/a/29936390/1364007). En su opinión, ¿la solución para las tres preguntas es exactamente la misma? –

+1

No lo reclamo. Si no es exacto, al menos está en el contexto. Mi intención es proporcionar una pista a la persona que enfrenta problemas similares. –

Cuestiones relacionadas