C# 如何将 TimeSpan 序列化为 XML

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/637933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 11:17:30  来源:igfitidea点击:

How to serialize a TimeSpan to XML

c#serializationtimespan

提问by joeldixon66

I am trying to serialize a .NET TimeSpanobject to XML and it is not working. A quick google has suggested that while TimeSpanis serializable, the XmlCustomFormatterdoes not provide methods to convert TimeSpanobjects to and from XML.

我正在尝试将 .NETTimeSpan对象序列化为XML,但它不起作用。一个快速的谷歌建议虽然TimeSpan是可序列化的,XmlCustomFormatter但不提供将TimeSpan对象转换为 XML 或从 XML转换的方法。

One suggested approach was to ignore the TimeSpanfor serialization, and instead serialize the result of TimeSpan.Ticks(and use new TimeSpan(ticks)for deserialization). An example of this follows:

一种建议的方法是忽略TimeSpanfor 序列化,而是将结果序列化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); }
    }
}

While this appears to work in my brief testing - is this the best way to achieve this?

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

Is there a better way to serialize a TimeSpan to and from XML?

有没有更好的方法将 TimeSpan 序列化为 XML 或从 XML 序列化?

采纳答案by Marc Gravell

The way you've already posted is probably the cleanest. If you don't like the extra property, you could implement IXmlSerializable, but then you have to do everything, which largely defeats the point. I'd happily use the approach you've posted; it is (for example) efficient (no complex parsing etc), culture independent, unambiguous, and timestamp-type numbers are easily and commonly understood.

您已经发布的方式可能是最干净的。如果你不喜欢额外的属性,你可以实现IXmlSerializable,但是你必须做所有的事情,这在很大程度上违背了这一点。我很乐意使用您发布的方法;它(例如)高效(没有复杂的解析等)、文化独立、明确且时间戳类型的数字很容易理解。

As an aside, I often add:

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

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

This just hides it in the UI and in referencing dlls, to avoid confusion.

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

回答by Rune Grimstad

A more readable option would be to serialize as a string and use the TimeSpan.Parsemethod to deserialize it. You could do the same as in your example but using TimeSpan.ToString()in the getter and TimeSpan.Parse(value)in the setter.

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

回答by Marc Gravell

Another option would be to serialize it using the SoapFormatterclass rather than the XmlSerializerclass.

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

The resulting XML file looks a little different...some "SOAP"-prefixed tags, etc...but it can do it.

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

Here's what SoapFormatterserialized a timespan of 20 hours and 28 minutes serialized to:

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

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

To use SOAPFormatter class, need to add reference to System.Runtime.Serialization.Formatters.Soapand use the namespace of the same name.

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

回答by phoog

You could create a light wrapper around the TimeSpan struct:

您可以围绕 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());
        }
    }
}

Sample serialized result:

示例序列化结果:

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

回答by Rory MacLeod

This is only a slight modification on the approach suggested in the question, but this Microsoft Connect issuerecommends using a property for serialization like this:

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

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

This would serialize a TimeSpan of 0:02:45 as:

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

<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>

Alternatively, the DataContractSerializersupports TimeSpan.

或者,DataContractSerializer支持 TimeSpan。

回答by Wes

Something that can work in some cases is to give your public property a backing field, which is a TimeSpan, but the public property is exposed as a string.

在某些情况下可行的方法是为您的公共财产提供一个支持字段,即 TimeSpan,但公共财产作为字符串公开。

eg:

例如:

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

This is ok if the property value is used mostly w/in the containing class or inheriting classes and is loaded from xml configuration.

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

The other proposed solutions are better if you want the public property to be a usable TimeSpan value for other classes.

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

回答by Manvendra

Try this :

尝试这个 :

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

回答by JRS

For data contract serialization I use the following.

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

  • Keeping the serialized property private keeps the public interface clean.
  • Using the public property name for serialization keeps the XML clean.
  • 保持序列化属性私有可以保持公共接口干净。
  • 使用公共属性名称进行序列化可以保持 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

回答by Mikhail

Combining an answer from Color serializationand this original solution(which is great by itself) I got this solution:

结合Color 序列化的答案和这个原始解决方案(这本身就很棒),我得到了这个解决方案:

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

where XmlTimeSpanclass is like this:

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

回答by Gildor

My version of the solution :)

我的解决方案版本:)

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

Edit: assuming it is nullable...

编辑:假设它可以为空...

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