Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / XML
Tip/Trick

Serialize System.TimeSpan to XML

Rate me:
Please Sign up or sign in to vote.
4.74/5 (9 votes)
22 Oct 2014CPOL3 min read 27.4K   6   2
Two ways to serialize and deserialize a property of type TimeSpan to XML

Introduction

As many before me have discovered, it is not possible to directly serialize an object of type TimeSpan using XmlSerializer.

This tip presents two ways to do it in an easy fashion.

There is no source code to download for this tip, but all code is included in the tip.

Background

My first approach to this problem, many years ago, was to create my own TimeSpan struct. As TimeSpan is a sealed struct and cannot be inherited, I had to implement all methods and properties in the original TimeSpan and there are quite a few when you start to look into it.

Another reason for doing this was also because in .NET 1.1, TimeSpan.ToString() contained a bug that omitted the millisecond part, if the value was less than 1 second. The effect of this bug was that values < 1 second were written as 'PT0S' when using DataSet.WriteXml. (This was fixed in .NET 2.0.)

Lessons learned from that experience were that it was a real pain to do and maintain.

Using the Code

One assumption is that you already have a class with a TimeSpan property that you want to serialize to an XML file or stream.

For further reading about XML Serialization, refer to this MSDN article: Introducing XML Serialization.

Solution 1

This is a very simple and straightforward solution and this is the biggest advantage.

What is done here is that one substitute property is added for the only purpose to be serialized. For all other purposes, the original property is used.

The original property is the one that will be visible for browsers and the substitute property will be serialized.

C#
// Original property
[XmlIgnore] // This attribute prevents the property from being serialized by the XmlSerializer
public TimeSpan ReadTimeout 
{
    get 
    { 
        return XmlConvert.ToTimeSpan(ReadTimeout_string);
    }
    set
    {
        ReadTimeout_string = XmlConvert.ToString(value);
    }
}

// Substitute property
[Browsable(false)] // Hides the property from, for example, a PropertyGrid
[XmlElement("ReadTimeout")] // Overrides the default name of property. 
                            // In this case ReadTimeout_string will become ReadTimeout 
public string ReadTimeout_string { get; set; }

Only public properties will be serialized, hence the string variant of ReadTimeout cannot be a private field.
The annoying part of this is that users of this class can also access the substitute property, which may be confusing.

Solution 2

In this case, we create a new class for the purpose of serialization.

This solution requires a little bit more work, but the major benefit is that this class can be reused and it makes for a more elegant solution as no substitute property has to be added.

C#
public class XmlTimeSpan
{
    private TimeSpan m_internal = TimeSpan.Zero;

    public XmlTimeSpan()
        : this(TimeSpan.Zero)
    {
    }

    public XmlTimeSpan(TimeSpan input)
    {
        m_internal = input;
    }

    public static implicit operator TimeSpan(XmlTimeSpan input)
    {
        return (input != null) ? input.m_internal : TimeSpan.Zero;
    }

    // Alternative to the implicit operator TimeSpan(XmlTimeSpan input)
    public TimeSpan ToTimeSpan()
    {
        return m_internal;
    }

    public static implicit operator XmlTimeSpan(TimeSpan input)
    {
        return new XmlTimeSpan(input);
    }

    // Alternative to the implicit operator XmlTimeSpan(TimeSpan input)
    public void FromTimeSpan(TimeSpan input)
    {
        this.m_internal = input;
    }

    [XmlText]
    public string Value
    {
        get
        {
            return XmlConvert.ToString(m_internal);
        }
        set
        {
            m_internal = XmlConvert.ToTimeSpan(value);
        }
    }
}

The XmlText attribute prevents an extra child node from being created. The output will be:

XML
<ReadTimeout>P2M5.500S</ReadTimeout>

instead of:

XML
<ReadTimeout>
    <Value>P2M5.5S</Value>
</ReadTimeout>

In this case, we only need one property in the class to be serialized.

The only thing that has to be added is the attribute XmlElement. This will redirect the serialization to use the class specified as the argument.

C#
[XmlElement(typeof(XmlTimeSpan))]
public TimeSpan ReadTimeout { get; set; }

Points of Interest

In both solutions, I have opted for saving the value as an xsd:duration string.

2 minutes and 4.5 seconds will be represented as PT2M4.5S. This is a personal choice and my reasoning is that it is more human readable.

Another option is to use TimeSpan.Ticks and save the value as a long.

C#
[XmlText]
public long Value
{
    get
    {
        return m_internal.Ticks;
    }
    set
    {
        m_internal = new TimeSpan(value);
    }
}

One can, of course, always argue why the data has to be human readable, but when comparing configuration files between each other, I find it easier and faster to find strange values when using the XML format compared to using ticks.

History

Revision Date Comment
1 2014-10-22 First release
2 2014-10-23 Corrected the XML formatting and some spelling errors

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Philippines Philippines
I have worked with mobile phone testing in manufacturing for 20 years.
Originally from Sweden, but moved to China in 2012.
At the moment I am on a sabbatical year in the Philippines.

Over the years I have used various programming languages such as Pascal, C, C++, C#, SQL, XML, XSLT.

Comments and Discussions

 
SuggestionAdditional methods for DataGridView data binding. Pin
Member 475507315-Feb-15 20:55
Member 475507315-Feb-15 20:55 
GeneralRe: Additional methods for DataGridView data binding. Pin
George Jonsson15-Feb-15 21:45
professionalGeorge Jonsson15-Feb-15 21:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.