How to serialize a TimeSpan to XML
Solution 1:
This is only a slight modification on the approach suggested in the question, but this Microsoft Connect issue recommends using a property for serialization like this:
[XmlIgnore]
public TimeSpan TimeSinceLastEvent
{
get { return m_TimeSinceLastEvent; }
set { m_TimeSinceLastEvent = value; }
}
// XmlSerializer does not support TimeSpan, so use this property for
// serialization instead.
[Browsable(false)]
[XmlElement(DataType="duration", ElementName="TimeSinceLastEvent")]
public string TimeSinceLastEventString
{
get
{
return XmlConvert.ToString(TimeSinceLastEvent);
}
set
{
TimeSinceLastEvent = string.IsNullOrEmpty(value) ?
TimeSpan.Zero : XmlConvert.ToTimeSpan(value);
}
}
This would serialize a TimeSpan of 0:02:45 as:
<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>
Alternatively, the DataContractSerializer
supports TimeSpan.
Solution 2:
The way you've already posted is probably the cleanest. If you don't like the extra property, you could implement IXmlSerializable
, but then you have to do everything, which largely defeats the point. I'd happily use the approach you've posted; it is (for example) efficient (no complex parsing etc), culture independent, unambiguous, and timestamp-type numbers are easily and commonly understood.
As an aside, I often add:
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
This just hides it in the UI and in referencing dlls, to avoid confusion.
Solution 3:
Something that can work in some cases is to give your public property a backing field, which is a TimeSpan, but the public property is exposed as a string.
eg:
protected TimeSpan myTimeout;
public string MyTimeout
{
get { return myTimeout.ToString(); }
set { myTimeout = TimeSpan.Parse(value); }
}
This is ok if the property value is used mostly w/in the containing class or inheriting classes and is loaded from xml configuration.
The other proposed solutions are better if you want the public property to be a usable TimeSpan value for other classes.
Solution 4:
Combining an answer from Color serialization and this original solution (which is great by itself) I got this solution:
[XmlElement(Type = typeof(XmlTimeSpan))]
public TimeSpan TimeSinceLastEvent { get; set; }
where XmlTimeSpan
class is like this:
public class XmlTimeSpan
{
private const long TICKS_PER_MS = TimeSpan.TicksPerMillisecond;
private TimeSpan m_value = TimeSpan.Zero;
public XmlTimeSpan() { }
public XmlTimeSpan(TimeSpan source) { m_value = source; }
public static implicit operator TimeSpan?(XmlTimeSpan o)
{
return o == null ? default(TimeSpan?) : o.m_value;
}
public static implicit operator XmlTimeSpan(TimeSpan? o)
{
return o == null ? null : new XmlTimeSpan(o.Value);
}
public static implicit operator TimeSpan(XmlTimeSpan o)
{
return o == null ? default(TimeSpan) : o.m_value;
}
public static implicit operator XmlTimeSpan(TimeSpan o)
{
return o == default(TimeSpan) ? null : new XmlTimeSpan(o);
}
[XmlText]
public long Default
{
get { return m_value.Ticks / TICKS_PER_MS; }
set { m_value = new TimeSpan(value * TICKS_PER_MS); }
}
}