Serializing a Nullable<DateTime> in to XML
Asked Answered
L

6

41

I am trying to serialize a class several of the data-members are Nullable objects, here is a example

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

However at runtime I get the error

Cannot serialize member 'AccountExpirationDate' of type System.Nullable`1[System.DateTime]. XmlAttribute/XmlText cannot be used to encode complex types.

However I checked and Nullable is a SerializableAttribute. What am I doing wrong?

Lumpkin answered 15/1, 2010 at 19:32 Comment(3)
Total guess, but have you tried DateTime? instead of Nullable<DateTime> ?Kendyl
@Terry, I can not as DateTime is not nullable and userPrincipal.AccountExpirationDate; can return a nullLumpkin
He was suggesting the shortcut notation DateTime?, which is synonymous with Nullable<DateTime>...Substantive
S
30

You can only serialize it as an XmlElement, not as an XmlAttribute, as the representation is too complex for an attribute. That's what the exception is telling you.

Substantive answered 15/1, 2010 at 19:35 Comment(4)
You can create your own implementation of Nullable<T> however and implement the necessary XML serialization attributes for it.Gusher
Yes, you could, agreed. But the compiler wouldn't treat it in exactly the same way as the built-in Nullabele<T>, which has special treatment (thinking of things like implicit definition of ==). So not recommended.Substantive
@NickLarsen - it also won't be able to correctly generate the schema unless you go mad with it.Salomon
multiple similar questions refer to the answer(s) given to this question. However, no answer (yet) gives me any clue on to 'why' the XmAttribute cannot simply be 'not serialized' when 'null' (using the ShouldSerializexxx paradigm). There is no reason why it should not work, hence the answer below by Marc is a verbose version of it. If we can type it, the serialization library could do it as well? Any clues on that?Systemize
S
48

If you just want it to work, then perhaps:

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

This will only include the attribute when there is a non-null value.

Salomon answered 15/1, 2010 at 20:52 Comment(3)
Does the XML Serializer check for a ShouldSerializeXxxxxxx method, or is it by catching the exception thrown by AccountExpirationDate.Value that it does not serialize? How does this work?Lumpkin
@Scott yes, ShouldSerialize* is a pattern used by multiple parts of the framework and multiple serialization librariesSalomon
What is about deserializing? If the attribute is not set in the XML, the nullable property should be null.Blaubok
S
30

You can only serialize it as an XmlElement, not as an XmlAttribute, as the representation is too complex for an attribute. That's what the exception is telling you.

Substantive answered 15/1, 2010 at 19:35 Comment(4)
You can create your own implementation of Nullable<T> however and implement the necessary XML serialization attributes for it.Gusher
Yes, you could, agreed. But the compiler wouldn't treat it in exactly the same way as the built-in Nullabele<T>, which has special treatment (thinking of things like implicit definition of ==). So not recommended.Substantive
@NickLarsen - it also won't be able to correctly generate the schema unless you go mad with it.Salomon
multiple similar questions refer to the answer(s) given to this question. However, no answer (yet) gives me any clue on to 'why' the XmAttribute cannot simply be 'not serialized' when 'null' (using the ShouldSerializexxx paradigm). There is no reason why it should not work, hence the answer below by Marc is a verbose version of it. If we can type it, the serialization library could do it as well? Any clues on that?Systemize
H
22

I've used something like this many times.

[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;
    }
}
Heilungkiang answered 16/1, 2010 at 3:48 Comment(1)
I think that should be: AccountExpirationDate.Value.ToString("yyyy/MM/dd HH:mm:ss.fff")Presignify
D
7

I was stuck into the similar problem. I had a datetime property (as XmlAttribute) in a class which was exposed in the WCF service.

Below is what I faced and the solution that worked for me : 1) XmlSerializer class was not serialising XmlAttribute of nullable type

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

2) Some posts suggest to replace [XmlAttribute] with [XmlElement(IsNullable =true)]. But this will serialize the Attribute as an Element which is totally useless. However it works fine for XmlElements

3) Some suggest to implement IXmlSerializable interface into your class, but that doesn't allow WCF service to be called from WCF consuming application. So this too does not work in this case.

Solution :

Don't mark property as nullable, instead use a ShouldSerializeXXX() method to put your constraint.

[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
}
Dorsy answered 29/4, 2015 at 6:56 Comment(3)
I notice you have posted exactly the same answer here, here, and here. In your opinion, is the solution for all three questions exactly the same?Lowdown
I don't claim it. If not exact, at least its in the context. My intention is to provide a hint to the person who is facing similar issues.Dorsy
"Don't mark property as nullable". So this does not answer the question since nullability is required.Fibrinolysin
S
1

Define a Serializable that encapsulates your funcionality.

Here's are and example.

[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
Schoenfeld answered 17/3, 2010 at 10:23 Comment(1)
This does not work, see #11636082Rourke
B
0

Simple workaround, which exposes one more property and is not very clean, but for simple cases works

        public bool? Nullable { get; set; }

        [XmlAttribute("nullable")]
        public string NullableXml
        {
            get => Nullable == null ? null : (bool)Nullable ? "true" : "false";
            set => Nullable = value == null ? (bool?)null : value == "true" ? true : value == "false" ? false : throw new Exception($"value {value} is not allowed");
        }
Biforked answered 1/6, 2021 at 10:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.