Сериализация Nullable <DateTime>в XML - PullRequest
34 голосов
/ 15 января 2010

Я пытаюсь сериализовать класс, некоторые из членов-данных являются объектами Nullable, вот пример

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

Однако во время выполнения я получаю ошибку

Невозможно сериализовать член 'AccountExpirationDate' типа System.Nullable`1 [System.DateTime]. XmlAttribute / XmlText нельзя использовать для кодирования сложных типов.

Однако я проверил, и Nullable является SerializableAttribute . Что я делаю неправильно?

Ответы [ 5 ]

43 голосов
/ 15 января 2010

Если вы просто хотите, чтобы работал , то, возможно:

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

Этот атрибут будет включаться только при наличии ненулевого значения.

28 голосов
/ 15 января 2010

Вы можете сериализовать его только как XmlElement, а не XmlAttribute, так как представление слишком сложно для атрибута. Вот что говорит вам исключение.

17 голосов
/ 16 января 2010

Я использовал что-то подобное много раз.

[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;
    }
}
3 голосов
/ 29 апреля 2015

Я застрял в подобной проблеме. У меня было свойство datetime (как XmlAttribute) в классе, который был представлен в службе WCF.

Ниже приведено то, с чем я столкнулся, и решение, которое сработало для меня: 1) Класс XmlSerializer не сериализовал атрибут XmlAttulble типа nullable

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

2) В некоторых сообщениях предлагается заменить [XmlAttribute] на [XmlElement (IsNullable = true)]. Но это сериализует Атрибут как Элемент, который совершенно бесполезен. Однако он прекрасно работает для XmlElements

3) Некоторые предлагают реализовать интерфейс IXmlSerializable в вашем классе, но это не позволяет вызывать службу WCF из приложения, потребляющего WCF. Так что это тоже не работает в этом случае.

Решение:

Не помечайте свойство как обнуляемое, вместо этого используйте метод ShouldSerializeXXX (), чтобы установить ограничение.

[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
}
2 голосов
/ 17 марта 2010

Определите Сериализуемый, который включает в себя вашу функциональность.

Вот и пример.

[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
...