C# 如何将TimeSpan序列化为XML

C# 如何将TimeSpan序列化为XML,c#,serialization,timespan,C#,Serialization,Timespan,我试图将.NETTimeSpan对象序列化为XML,但它不起作用。一个快速的google建议,虽然TimeSpan是可序列化的,但XmlCustomFormatter不提供将TimeSpan对象与XML进行转换的方法 一种建议的方法是忽略用于序列化的TimeSpan,而是序列化TimeSpan.Ticks的结果(并使用newtimespan(Ticks)进行反序列化)。这方面的一个例子如下: [Serializable] public class MyClass { // Local V

我试图将.NET
TimeSpan
对象序列化为XML,但它不起作用。一个快速的google建议,虽然
TimeSpan
是可序列化的,但
XmlCustomFormatter
不提供将
TimeSpan
对象与XML进行转换的方法

一种建议的方法是忽略用于序列化的
TimeSpan
,而是序列化
TimeSpan.Ticks
的结果(并使用
newtimespan(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和从XML序列化?

更可读的方法是将TimeSpan序列化为字符串,并使用
TimeSpan.Parse
方法对其进行反序列化。您可以执行与示例中相同的操作,但是在getter中使用
TimeSpan.ToString()
,在setter中使用
TimeSpan.Parse(value)

您已经发布的方式可能是最干净的。如果您不喜欢额外的属性,可以实现
IXmlSerializable
,但是接下来您必须执行所有操作,这在很大程度上违背了这一点。我很乐意使用您发布的方法;它(例如)高效(无复杂解析等),与区域性无关,明确无误,并且时间戳类型的数字易于理解

作为旁白,我经常补充:

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

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

另一个选项是使用
SoapFormatter
类而不是
XmlSerializer
类对其进行序列化

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

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

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>
P0Y0M0DT20H28M0S

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

您可以在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>

2010-12-06T08:45:12.5
2.08:29:35.2500000

这只是对问题中建议的方法的轻微修改,但建议使用如下属性进行序列化:

[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); 
    }
}
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); }
    }
}
这会将0:02:45的时间跨度序列化为:

<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>
PT2M45S

或者,
DataContractSerializer
支持时间跨度。

在某些情况下,可以为公共属性提供一个支持字段,该字段是时间跨度,但公共属性以字符串形式公开

例如:

如果属性值主要在包含类或继承类中使用,并且是从xml配置加载的,则可以这样做

如果希望公共属性成为其他类的可用时间跨度值,则建议的其他解决方案更好。

尝试以下方法:

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

对于数据协定序列化,我使用以下方法

  • 保持序列化属性的私有性可以保持公共接口干净
  • 使用公共属性名进行序列化可以保持XML的干净
公共属性持续时间作为时间跨度
私有属性DurationString作为字符串
得到
返回Duration.ToString
结束
设置(值为字符串)
持续时间=TimeSpan.Parse(值)
端集
端属性
结合和的答案(这本身就很好),我得到了这个解决方案:

[XmlElement(Type = typeof(XmlTimeSpan))]
public TimeSpan TimeSinceLastEvent { get; set; }
其中
XmlTimeSpan
类如下:

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

我的解决方案版本:)

编辑:假设它可以为空

[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):

还有更全面的例子:

查看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);
        }
使用(var fs=new FileStream(“file.xml”,FileMode.Create))
{
var serializer=新的DataContractSerializer(typeof(List));
serializer.WriteObject(fs,_项);
}

这就是它在.net 4.0中的序列化方式如果您在包装System.TimeSpan的结构上实现接口,而不是在MyClass上实现接口,那么一切都不会那么糟糕。然后,您只需更改MyClass.TimeSinceLastEvent属性的类型,这是迄今为止最简单的解决方案。我已经想出了完全相同的东西,它就像一个魅力。易于实现和理解。这是最好的解决方案。它连载得很好!!!谢谢你的输入朋友!知道如何将输出作为XmlAttribute吗?@ala,如果我正确理解了您的问题,答案是将XmlAttributeAttribute应用于要表示为属性的属性。当然,这不是TimeSpan所特有的。+1很好,只是我不会将其序列化为字符串,但
会标记
尽可能长。@ChrisWue在我的办公室里,当我们想要可读的输出时,我们使用xml序列化;将时间跨度序列化为long与该目标不太兼容。当然,如果您出于不同的原因使用xml序列化,序列化记号可能更有意义。+1表示XmlConvert.ToTimeSpan()。它处理时间跨度(如PT2H15M)的ISO标准持续时间语法,如果我错了,请参阅更正我,但serlized timespan“PT2M45S”是00:02:45,而不是2:45:00。连接链接现在已断开,可能可以替换为以下链接:?这项技术看起来也有点不同……Rory MacLeod下面的回答实际上是Microsoft推荐的方法。我不会对TimeSpand使用长刻度,因为XML的持续时间类型是完全匹配的。这个问题在2008年被提交给微软,但从未解决。有一个解决方案文档
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));
            }
       }
    }
}
        using (var fs = new FileStream("file.xml", FileMode.Create))
        {
            var serializer = new DataContractSerializer(typeof(List<SomeType>));
            serializer.WriteObject(fs, _items);
        }