如何将 TimeSpan 序列化为 XML

发布于 2024-07-15 02:55:24 字数 1013 浏览 13 评论 0原文

我正在尝试将 .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 进行序列化?

I am trying to serialize a .NET TimeSpan object to XML and it is not working. A quick google has suggested that while TimeSpan is serializable, the XmlCustomFormatter does not provide methods to convert TimeSpan objects to and from XML.

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

[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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

星光不落少年眉 2024-07-22 02:55:24

这只是对问题中建议的方法的轻微修改,但是 此 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); 
    }
}

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

<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>

或者,DataContractSerializer 支持时间跨度。

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

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

<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>

Alternatively, the DataContractSerializer supports TimeSpan.

-柠檬树下少年和吉他 2024-07-22 02:55:24

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

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

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

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

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.

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.

山田美奈子 2024-07-22 02:55:24

在某些情况下,可以为您的公共属性提供一个支持字段,即 TimeSpan,但公共属性以字符串形式公开。

例如:

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

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

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

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.

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.

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

儭儭莪哋寶赑 2024-07-22 02:55:24

结合 颜色序列化这个原始解决方案(本身就很棒)我得到了这个解决方案:

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

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

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

where XmlTimeSpan class is like this:

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); }
    }
}
冷…雨湿花 2024-07-22 02:55:24

您可以围绕 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>

You could create a light wrapper around the TimeSpan struct:

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>
自控 2024-07-22 02:55:24

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

A more readable option would be to serialize as a string and use the TimeSpan.Parse method 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.

浅语花开 2024-07-22 02:55:24

对于 .NET6 和 .NET7,TimeSpan 序列化可以开箱即用。 该格式是 XSD“duration”数据类型的格式。 因此“14:30”被序列化为 PT14H30M

对于 .NET Framework 4.8,可以使用以下开关激活此行为:

AppContext.SetSwitch("Switch.System.Xml.EnableTimeSpanSerialization", true);

For .NET6 and .NET7, TimeSpan serialization works out of the box. The format is the format for XSD "duration" datatype. So "14:30" is serialized to PT14H30M

For .NET Framework 4.8, this behavior can be activated with this switch:

AppContext.SetSwitch("Switch.System.Xml.EnableTimeSpanSerialization", true);
深居我梦 2024-07-22 02:55:24

我的解决方案版本。

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

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;
    }
}
愛上了 2024-07-22 02:55:24

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

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

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

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

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

Another option would be to serialize it using the SoapFormatter class rather than the XmlSerializer class.

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

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

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

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

稀香 2024-07-22 02:55:24

时间跨度以秒数形式存储在 xml 中,但我希望它很容易采用。
手动序列化时间跨度(实现 IXmlSerialized):

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 有一些棘手的代码。

Timespan stored in xml as number of seconds, but it is easy to adopt, I hope.
Timespan serialized manually (implementing 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));
            }
       }
    }
}

There is more comprehensive example:
https://bitbucket.org/njkazakov/timespan-serialization

Look at Settings.cs.
And there is some tricky code to use XmlElementAttribute.

孤星 2024-07-22 02:55:24

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

If you do not want any workarounds, use the DataContractSerializer class from System.Runtime.Serialization.dll.

        using (var fs = new FileStream("file.xml", FileMode.Create))
        {
            var serializer = new DataContractSerializer(typeof(List<SomeType>));
            serializer.WriteObject(fs, _items);
        }

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

  • 保持序列化属性私有可以保持公共接口的干净。
  • 使用公共属性名称进行序列化可以保持 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

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.
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
青柠芒果 2024-07-22 02:55:24

尝试这个 :

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

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); }
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文