How to serialize a TimeSpan to XML

C#SerializationTimespan

C# Problem Overview


I am trying to serialize a .NET TimeSpan object to XML and it is not working. A quick google has suggested that while TimeSpan is serializable, the XmlCustomFormatter does not provide methods to convert TimeSpan objects to and from XML.

One suggested approach was to ignore the TimeSpan for serialization, and instead serialize the result of TimeSpan.Ticks (and use new TimeSpan(ticks) for deserialization). An example of this follows:

[Serializable]
public class MyClass
{
    // Local Variable
    private TimeSpan m_TimeSinceLastEvent;

    // Public Property - XmlIgnore as it doesn't serialize anyway
    [XmlIgnore]
    public TimeSpan TimeSinceLastEvent
    {
        get { return m_TimeSinceLastEvent; }
        set { m_TimeSinceLastEvent = value; }
    }

    // Pretend property for serialization
    [XmlElement("TimeSinceLastEvent")]
    public long TimeSinceLastEventTicks
    {
        get { return m_TimeSinceLastEvent.Ticks; }
        set { m_TimeSinceLastEvent = new TimeSpan(value); }
    }
}

While this appears to work in my brief testing - is this the best way to achieve this?

Is there a better way to serialize a TimeSpan to and from XML?

C# Solutions


Solution 1 - C#

This is only a slight modification on the approach suggested in the question, but [this Microsoft Connect issue][1] 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.

[1]: http://connect.microsoft.com/VisualStudio/feedback/details/386602/system-timespan-xml-serialization "Microsoft Connect"

Solution 2 - C#

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 - C#

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 - C#

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

Solution 5 - C#

You could create a light wrapper around the TimeSpan struct:

namespace My.XmlSerialization
{
    public struct TimeSpan : IXmlSerializable
    {
        private System.TimeSpan _value;

        public static implicit operator TimeSpan(System.TimeSpan value)
        {
            return new TimeSpan { _value = value };
        }

        public static implicit operator System.TimeSpan(TimeSpan value)
        {
            return value._value;
        }

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            _value = System.TimeSpan.Parse(reader.ReadContentAsString());
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteValue(_value.ToString());
        }
    }
}

Sample serialized result:

<Entry>
  <StartTime>2010-12-06T08:45:12.5</StartTime>
  <Duration>2.08:29:35.2500000</Duration>
</Entry>

Solution 6 - C#

A more readable option would be to serialize as a string and use the TimeSpan.Parse method to deserialize it. You could do the same as in your example but using TimeSpan.ToString() in the getter and TimeSpan.Parse(value) in the setter.

Solution 7 - C#

Another option would be to serialize it using the SoapFormatter class rather than the XmlSerializer class.

The resulting XML file looks a little different...some "SOAP"-prefixed tags, etc...but it can do it.

Here's what SoapFormatter serialized a timespan of 20 hours and 28 minutes serialized to:

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

To use SOAPFormatter class, need to add reference to System.Runtime.Serialization.Formatters.Soap and use the namespace of the same name.

Solution 8 - C#

My version of the solution :)

[DataMember, XmlIgnore]
public TimeSpan MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
    get { return MyTimeoutValue.ToString(); }
    set { MyTimeoutValue = TimeSpan.Parse(value); }
}

Edit: assuming it is nullable...

[DataMember, XmlIgnore]
public TimeSpan? MyTimeoutValue { get; set; }
[DataMember]
public string MyTimeout
{
    get 
    {
        if (MyTimeoutValue != null)
            return MyTimeoutValue.ToString();
        return null;
    }
    set 
    {
        TimeSpan outValue;
        if (TimeSpan.TryParse(value, out outValue))
            MyTimeoutValue = outValue;
        else
            MyTimeoutValue = null;
    }
}

Solution 9 - C#

Timespan stored in xml as number of seconds, but it is easy to adopt, I hope. Timespan serialized manually (implementing IXmlSerializable):

public class Settings : IXmlSerializable
{
	[XmlElement("IntervalInSeconds")]
	public TimeSpan Interval;

	public XmlSchema GetSchema()
	{
		return null;
    }

 	public void WriteXml(XmlWriter writer)
	{
    	writer.WriteElementString("IntervalInSeconds", ((int)Interval.TotalSeconds).ToString());
    }

	public void ReadXml(XmlReader reader)
	{
		string element = null;
		while (reader.Read())
		{
			if (reader.NodeType == XmlNodeType.Element)
				element = reader.Name;
			else if (reader.NodeType == XmlNodeType.Text)
	  		{
				if (element == "IntervalInSeconds")
					Interval = TimeSpan.FromSeconds(double.Parse(reader.Value.Replace(',', '.'), CultureInfo.InvariantCulture));
	  		}
	   }
	}
}

There is more comprehensive example: https://bitbucket.org/njkazakov/timespan-serialization

Look at Settings.cs. And there is some tricky code to use XmlElementAttribute.

Solution 10 - C#

If you do not want any workarounds, use the DataContractSerializer class from System.Runtime.Serialization.dll.

        using (var fs = new FileStream("file.xml", FileMode.Create))
        {
            var serializer = new DataContractSerializer(typeof(List<SomeType>));
            serializer.WriteObject(fs, _items);
        }

Solution 11 - C#

For data contract serialization I use the following.

  • Keeping the serialized property private keeps the public interface clean.
  • Using the public property name for serialization keeps the XML clean.

Public Property Duration As TimeSpan

<DataMember(Name:="Duration")>
Private Property DurationString As String
    Get
        Return Duration.ToString
    End Get
    Set(value As String)
        Duration = TimeSpan.Parse(value)
    End Set
End Property

   

Solution 12 - C#

Try this :

//Don't Serialize Time Span object.
        [XmlIgnore]
        public TimeSpan m_timeSpan;
//Instead serialize (long)Ticks and instantiate Timespan at time of deserialization.
        public long m_TimeSpanTicks
        {
            get { return m_timeSpan.Ticks; }
            set { m_timeSpan = new TimeSpan(value); }
        }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionjoeldixon66View Question on Stackoverflow
Solution 1 - C#Rory MacLeodView Answer on Stackoverflow
Solution 2 - C#Marc GravellView Answer on Stackoverflow
Solution 3 - C#WesView Answer on Stackoverflow
Solution 4 - C#MikhailView Answer on Stackoverflow
Solution 5 - C#phoogView Answer on Stackoverflow
Solution 6 - C#Rune GrimstadView Answer on Stackoverflow
Solution 7 - C#View Answer on Stackoverflow
Solution 8 - C#Simone S.View Answer on Stackoverflow
Solution 9 - C#askazakovView Answer on Stackoverflow
Solution 10 - C#Sasha YakobchukView Answer on Stackoverflow
Solution 11 - C#JRSView Answer on Stackoverflow
Solution 12 - C#ManvendraView Answer on Stackoverflow