我试图将. net TimeSpan对象序列化为XML,但它不起作用。快速谷歌表明,虽然TimeSpan是可序列化的,但XmlCustomFormatter没有提供将TimeSpan对象转换为XML或从XML转换的方法。

一种建议的方法是忽略TimeSpan进行序列化,而是序列化TimeSpan的结果。刻度(并使用新的TimeSpan(刻度)进行反序列化)。下面是一个例子:

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

虽然这在我的简短测试中似乎是有效的,但这是实现这一目标的最佳方式吗?

是否有更好的方法将TimeSpan与XML进行序列化?


当前回答

我的解决方案。

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

编辑:假设它是空的:

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

其他回答

你已经发布的方式可能是最干净的。如果您不喜欢这个额外的属性,您可以实现IXmlSerializable,但是您必须完成所有的事情,这在很大程度上违背了重点。我很乐意使用你发布的方法;例如,它是高效的(没有复杂的解析等)、区域性独立、无歧义的,并且时间戳类型的数字易于理解和普遍理解。

顺便说一句,我经常补充:

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]

这只是将它隐藏在UI和引用dll中,以避免混淆。

在某些情况下可行的方法是给你的公共属性一个支持字段,它是一个TimeSpan,但是公共属性被暴露为一个字符串。

eg:

protected TimeSpan myTimeout;
public string MyTimeout 
{ 
    get { return myTimeout.ToString(); } 
    set { myTimeout = TimeSpan.Parse(value); }
}

如果属性值主要在包含类或继承类中使用w/,并且从xml配置中加载,那么这是可以的。

如果您希望公共属性是其他类可用的TimeSpan值,那么其他建议的解决方案会更好。

Timespan以秒数的形式存储在xml中,但是我希望它很容易采用。 手动序列化的时间跨度(实现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));
            }
       }
    }
}

还有一个更全面的例子: https://bitbucket.org/njkazakov/timespan-serialization

查看Settings.cs。 使用XmlElementAttribute需要一些复杂的代码。

如果不需要任何变通方法,请使用System.Runtime.Serialization.dll中的DataContractSerializer类。

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

另一种选择是使用SoapFormatter类而不是XmlSerializer类来序列化它。

结果的XML文件看起来有点不同…一些“SOAP”前缀标签,等等……但它可以做到。

下面是SoapFormatter将一个20小时28分钟的时间跨度序列化为:

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

要使用SOAPFormatter类,需要添加对System.Runtime.Serialization.Formatters.Soap的引用,并使用同名的命名空间。