我正在尝试将.NET TimeSpan
对象序列化为XML,但它无效。谷歌快速建议虽然TimeSpan
是可序列化的,但XmlCustomFormatter
不提供将TimeSpan
对象转换为XML的方法。
一种建议的方法是忽略序列化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); }
}
}
虽然这似乎适用于我的简短测试 - 这是实现这一目标的最佳方式吗?
有没有更好的方法将TimeSpan序列化为XML?
答案 0 :(得分:94)
这只是对问题中建议的方法稍作修改,但this Microsoft Connect issue建议使用属性进行序列化,如下所示:
[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序列化为:
<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>
或者,DataContractSerializer
支持TimeSpan。
答案 1 :(得分:68)
您已经发布的方式可能是最干净的。如果你不喜欢额外的财产,你可以实施IXmlSerializable
,但是你必须做一切,这在很大程度上打败了这一点。我很乐意使用你发布的方法;它(例如)有效(没有复杂的解析等),独立于文化,明确无误,时间戳类型的数字很容易被普遍理解。
顺便说一句,我经常补充说:
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
这只是隐藏在UI和引用dll中,以避免混淆。
答案 2 :(得分:28)
在某些情况下可以起作用的是为您的公共属性提供一个支持字段,即TimeSpan,但公共属性是作为字符串公开的。
例如:
protected TimeSpan myTimeout;
public string MyTimeout
{
get { return myTimeout.ToString(); }
set { myTimeout = TimeSpan.Parse(value); }
}
如果属性值主要在包含类或继承类中使用并从xml配置加载,则可以。
如果您希望公共属性成为其他类的可用TimeSpan值,则其他建议的解决方案会更好。
答案 3 :(得分:22)
结合Color serialization和this original solution的答案(这本身就很棒)我得到了这个解决方案:
[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); }
}
}
答案 4 :(得分:9)
您可以在TimeSpan结构周围创建一个光包装器:
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>
答案 5 :(得分:8)
更可读的选项是序列化为字符串并使用TimeSpan.Parse
方法对其进行反序列化。您可以在示例中执行相同操作,但在getter中使用TimeSpan.ToString()
,在setter中使用TimeSpan.Parse(value)
。
答案 6 :(得分:2)
另一种选择是使用SoapFormatter
类而不是XmlSerializer
类来序列化它。
生成的XML文件看起来有点不同......一些“SOAP” - 前缀标签等......但它可以做到。
以下是SoapFormatter
将20小时28分钟的序列序列化为:
<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>
要使用SOAPFormatter类,需要添加对System.Runtime.Serialization.Formatters.Soap
的引用并使用相同名称的命名空间。
答案 7 :(得分:1)
我的解决方案版本:)
[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;
}
}
答案 8 :(得分:1)
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。
答案 9 :(得分:0)
对于数据合同序列化,我使用以下内容。
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
答案 10 :(得分:0)
如果您不想要任何变通办法,请使用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);
}
答案 11 :(得分:-2)
试试这个:
//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); }
}