How to serialize a TimeSpan to XML
我正试图将.NET
一种建议的方法是忽略用于序列化的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | [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); } } } |
虽然这在我的简短测试中似乎有效-这是实现这一目标的最佳方法吗?
是否有更好的方法来序列化XML之间的时间跨度?
这只是对问题中建议的方法的一个细微修改,但此Microsoft Connect问题建议使用如下的属性进行序列化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [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的时间跨度序列化为:
1 | <TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent> |
。
或者,
你已经发布的方式可能是最干净的。如果你不喜欢这个额外的属性,你可以实现
作为旁白,我经常补充:
1 | [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] |
这只是将其隐藏在UI和引用DLL中,以避免混淆。
在某些情况下可以工作的是为公共属性提供一个支持字段,它是一个时间跨度,但是公共属性作为字符串公开。
如:
1 2 3 4 5 6 | protected TimeSpan myTimeout; public string MyTimeout { get { return myTimeout.ToString(); } set { myTimeout = TimeSpan.Parse(value); } } |
。
如果属性值主要在包含类或继承类中使用,并且从XML配置加载,则这是正常的。
如果希望公共属性是其他类的可用时间跨度值,则其他建议的解决方案更好。
结合颜色序列化的答案和这个原始解决方案(本身很好),我得到了这个解决方案:
1 2 |
其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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结构创建一个light wrapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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()); } } } |
示例序列化结果:
1 2 3 4 | <Entry> <StartTime>2010-12-06T08:45:12.5</StartTime> <Duration>2.08:29:35.2500000</Duration> </Entry> |
。
一个更可读的选项是作为字符串进行序列化,并使用
另一种选择是使用
生成的XML文件看起来有点不同……一些"soap"前缀标记等……但它可以做到。
以下是
1 | <myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan> |
号
要使用SoapFormatter类,需要添加对
时间跨度以秒为单位存储在XML中,但我希望它易于采用。手动序列化的TimeSpan(实现IXML可序列化):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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://bitback.org/njkazakov/timespan-serialization
看看settings.cs。还有一些使用xmlementattribute的复杂代码。
我的解决方案版本:)
1 2 3 4 5 6 7 8 | [DataMember, XmlIgnore] public TimeSpan MyTimeoutValue { get; set; } [DataMember] public string MyTimeout { get { return MyTimeoutValue.ToString(); } set { MyTimeoutValue = TimeSpan.Parse(value); } } |
号
编辑:假设它可以为空…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | [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; } } |
号
如果不需要任何解决方法,请使用System.Runtime.Serialization.dll中的DataContractSerializer类。
1 2 3 4 5 |
号
对于数据协定序列化,我使用以下内容。
- 保持序列化属性为私有将保持公共接口的干净。
- 使用公共属性名进行序列化可以保持XML的整洁。
1 2 3 4 5 6 7 8 9 10 11 | 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 |
。
试试这个:
1 2 3 4 5 6 7 8 9 | //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); } } |