如何将TimeSpan序列化为XML

我想序列化一个.NET TimeSpan对象到XML,它不工作。 一个快速的谷歌已经build议,虽然TimeSpan是可序列化的,但XmlCustomFormatter不提供将TimeSpan对象与XML进行转换的方法。

一个build议的方法是忽略TimeSpan进行序列化,而是序列化TimeSpan.Ticks的结果(并使用new TimeSpan(ticks)进行反序列化)。 下面是一个例子:

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

虽然这似乎在我的简短testing工作 – 这是实现这一目标的最好方法?

有没有更好的方法来序列化TimeSpan和XML?

你已经发布的方式可能是最干净的。 如果你不喜欢这个额外的属性,你可以实现IXmlSerializable ,但是你必须做所有的事情 ,这在很大程度上是不利的。 我很乐意使用你发布的方法。 它是(例如)有效的(没有复杂的parsing等),文化独立,毫不含糊,时间戳types的数字是容易和普遍的理解。

另外,我经常补充说:

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

这只是隐藏在用户界面和参考dll,以避免混淆。

这只是对问题中build议的方法稍作修改,但此Microsoft Connect问题build议使用属性进行序列化,如下所示:

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

这会将0:02:45的TimeSpan序列化为:

 <TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent> 

或者, DataContractSerializer支持TimeSpan。

在某些情况下可以工作的东西是给你的公共属性一个支持领域,这是一个TimeSpan,但公共属性暴露为一个string。

例如:

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

如果属性值主要在包含的类或inheritance类中使用,并且从xmlconfiguration加载,则这是可以的。

如果您希望公有属性成为其他类的可用TimeSpan值,则其他build议的解决scheme会更好。

结合颜色序列化和这个原始的解决scheme (这是伟大的答案)我得到了这个解决scheme:

 [XmlElement(Type = typeof(XmlTimeSpan))] public TimeSpan TimeSinceLastEvent { get; set; } 

XmlTimeSpan类是这样的:

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

你可以在TimeSpan结构体周围创build一个简单的包装器:

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

样本序列化结果:

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

更可读的选项是序列化为string,并使用TimeSpan.Parse方法对其进行反序列化。 你可以像在你的例子中那样做,但是在getter中使用TimeSpan.ToString() ,在setter中使用TimeSpan.Parse(value)

另一种select是使用SoapFormatter类而不是XmlSerializer类对其进行序列化。

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

SoapFormatter序列化了20小时28分钟的序列化时间:

 <myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan> 

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

我的版本的解决scheme:)

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

Timespan存储在xml中的秒数,但很容易通过,我希望。 Timespan手动序列化(实现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。

对于数据协定序列化,我使用以下内容。

  • 保持序列化的属性私人保持公共接口清洁。
  • 使用公共属性名称进行序列化保持XML清洁。
 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 

无法评论或排名,但评论SoapDuration

 [XmlElement, Type=SoapDuration] public TimeSpan TimeSinceLastEvent 

要么

 public SoapDuration TimeSinceLastEventTicks 

如果您不需要任何解决方法,请使用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); } 

尝试这个 :

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