Cách tuần tự hóa TimeSpan thành XML


206

Tôi đang cố gắng tuần tự hóa một TimeSpanđối tượng .NET thành XML và nó không hoạt động. Một google nhanh chóng đã gợi ý rằng trong khi TimeSpancó thể tuần tự hóa, thì XmlCustomFormatternó không cung cấp các phương thức để chuyển đổi TimeSpancác đối tượng sang và từ XML.

Một cách tiếp cận được đề xuất là bỏ qua việc TimeSpantuần tự hóa, và thay vào đó tuần tự hóa kết quả của TimeSpan.Ticks(và sử dụng new TimeSpan(ticks)để giải tuần tự hóa ). Một ví dụ về điều này như sau:

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

Trong khi điều này dường như hoạt động trong thử nghiệm ngắn gọn của tôi - đây có phải là cách tốt nhất để đạt được điều này?

Có cách nào tốt hơn để tuần tự hóa TimeSpan đến và từ XML không?


4
Câu trả lời của Rory MacLeod dưới đây thực sự là cách Microsoft khuyên bạn nên làm điều này.
Jeff

2
Tôi sẽ không sử dụng các đánh dấu dài cho TimeSpand vì loại thời lượng của XML là khớp chính xác. Vấn đề đã được đưa ra cho Microsoft vào năm 2008 nhưng không bao giờ được giải quyết. Có một cách giải quyết được ghi lại sau đó: kennethxu.blogspot.com/2008/09/ Khăn
Kenneth Xu

Câu trả lời:


71

Cách bạn đã đăng có lẽ là sạch nhất. Nếu bạn không thích tài sản bổ sung, bạn có thể thực hiện IXmlSerializable, nhưng sau đó bạn phải làm mọi thứ , điều này phần lớn đánh bại điểm. Tôi vui vẻ sử dụng cách tiếp cận bạn đã đăng; đó là (ví dụ) hiệu quả (không phân tích cú pháp phức tạp, v.v.), các số kiểu độc lập, không rõ ràng và dấu thời gian văn hóa dễ dàng và dễ hiểu.

Như một bên, tôi thường thêm:

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

Điều này chỉ ẩn nó trong UI và trong các dll tham chiếu, để tránh nhầm lẫn.


5
Làm mọi thứ không tệ lắm nếu bạn triển khai giao diện trên một cấu trúc bao bọc System.TimeSpan, thay vì triển khai nó trên MyClass. Sau đó, bạn chỉ phải thay đổi loại trên thuộc tính MyClass.TimeSinceLastEvent của mình
phoog

103

Đây chỉ là một sửa đổi nhỏ về cách tiếp cận được đề xuất trong câu hỏi, nhưng vấn đề Microsoft Connect này khuyên bạn nên sử dụng một thuộc tính để tuần tự hóa như thế này:

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

Điều này sẽ tuần tự hóa một TimeSpan là 0:02:45 như:

<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>

Ngoài ra, DataContractSerializerhỗ trợ TimeSpan.


15
+1 cho XmlConvert.ToTimeSpan (). Nó xử lý cú pháp thời lượng tiêu chuẩn ISO cho thời gian như PT2H15M, xem en.wikipedia.org/wiki/ISO_8601#Durations
yzorg

2
Chỉnh sửa cho tôi nếu tôi sai, nhưng TimeSpan "PT2M45S" được serl hóa là 00:02:45, không phải 2:45:00.
Tom Pažourek

Liên kết kết nối hiện đã bị hỏng, có lẽ nó có thể được thay thế bằng liên kết này: connect.microsoft.com/VisualStudio/feedback/details/684819/ Lỗi ? Kỹ thuật này cũng có vẻ hơi khác ...
TJB

Một câu hỏi kỳ lạ để theo dõi, chúng ta có cách nào để giải lại giá trị PT2M45S này theo thời gian trong SQL không?
Xander

28

Một số thứ có thể hoạt động trong một số trường hợp là cung cấp cho tài sản công cộng của bạn một trường sao lưu, đó là TimeSpan, nhưng thuộc tính công khai được hiển thị dưới dạng chuỗi.

ví dụ:

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

Điều này là ổn nếu giá trị thuộc tính được sử dụng chủ yếu là trong lớp chứa hoặc lớp kế thừa và được tải từ cấu hình xml.

Các giải pháp đề xuất khác sẽ tốt hơn nếu bạn muốn tài sản công cộng là giá trị TimeSpan có thể sử dụng được cho các lớp khác.


Cho đến nay các giải pháp dễ dàng nhất. Tôi đã đưa ra chính xác điều tương tự và nó hoạt động như một lá bùa. Dễ dàng thực hiện và hiểu.
wpfwannabe

1
Đây là giải pháp tốt nhất ở đây. Nó nối tiếp rất tốt !!! Cảm ơn bạn cho bạn đầu vào!
Nhà phát triển

25

Kết hợp một câu trả lời từ tuần tự hóa màugiải pháp ban đầu này (rất tuyệt vời) tôi đã có giải pháp này:

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

nơi XmlTimeSpanlớp là như thế này:

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

Cách tốt nhất và dễ dàng để giải quyết vấn đề này ... đối với tôi
Moraru Viorel

Điều này là hoàn toàn khéo léo - Tôi siêu ấn tượng!
Jim

9

Bạn có thể tạo một trình bao bọc ánh sáng xung quanh cấu trúc 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());
        }
    }
}

Kết quả tuần tự mẫu:

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

Bất kỳ ý tưởng làm thế nào để làm cho đầu ra là XmlAttribution?
ala

@ala, Nếu tôi hiểu chính xác câu hỏi của bạn, câu trả lời là áp dụng XmlAttributionAttribution cho thuộc tính bạn muốn thể hiện dưới dạng một thuộc tính. Điều đó không đặc biệt với TimeSpan, tất nhiên.
phoog

+1 Đẹp, ngoại trừ tôi sẽ không xâu chuỗi nó thành chuỗi mà Tickslà dài.
ChrisWue

@ChrisWue Trong văn phòng của tôi, chúng tôi sử dụng xml serialization khi chúng tôi muốn đầu ra có thể đọc được của con người; tuần tự hóa một khoảng thời gian dài không hoàn toàn tương thích với mục tiêu đó. Tất nhiên, nếu bạn sử dụng xml serialization vì một lý do khác, việc tuần tự hóa các tick có thể có ý nghĩa hơn.
phoog

8

Một tùy chọn dễ đọc hơn sẽ là tuần tự hóa thành một chuỗi và sử dụng TimeSpan.Parsephương thức để giải tuần tự hóa nó. Bạn có thể làm tương tự như trong ví dụ của bạn nhưng sử dụng TimeSpan.ToString()trong getter và TimeSpan.Parse(value)trong setter.


2

Một tùy chọn khác là tuần tự hóa nó bằng cách sử dụng SoapFormatterlớp chứ không phải làXmlSerializer lớp.

Tệp XML kết quả trông hơi khác một chút ... một số thẻ được thêm vào "SOAP", v.v ... nhưng nó có thể làm được.

Đây là những gì SoapFormatterđược tuần tự hóa một khoảng thời gian 20 giờ và 28 phút được tuần tự hóa thành:

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

Để sử dụng lớp SOAPFormatter, cần thêm tham chiếu System.Runtime.Serialization.Formatters.Soapvà sử dụng không gian tên cùng tên.


Đây là cách nó tuần tự hóa trong .net 4.0
Kirk Broadhurst

1

Phiên bản của tôi về giải pháp :)

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

Chỉnh sửa: giả sử nó là 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;
    }
}

1

Timespan được lưu trữ trong xml dưới dạng số giây, nhưng nó rất dễ chấp nhận, tôi hy vọng. Timespan được tuần tự hóa thủ công (triển khai 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));
            }
       }
    }
}

Có ví dụ toàn diện hơn: https://bitbucket.org/njkazakov/timespan-serialization

Nhìn vào Cài đặt.cs. Và có một số mã khó sử dụng XmlEuityAttribution.


1
Vui lòng trích dẫn các thông tin liên quan từ liên kết đó. Tất cả thông tin cần thiết cho câu trả lời của bạn nên có trên trang web này và sau đó bạn có thể trích dẫn liên kết đó dưới dạng nguồn.
SuperBiasedMan

0

Đối với tuần tự hợp đồng dữ liệu tôi sử dụng như sau.

  • Giữ tài sản tuần tự riêng tư giữ cho giao diện công cộng sạch sẽ.
  • Sử dụng tên thuộc tính công cộng để tuần tự hóa giữ cho XML sạch.
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

0

Nếu bạn không muốn bất kỳ cách giải quyết nào, hãy sử dụng lớp DataContractSerializer từ System.R.78.Serialization.dll.

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

-2

Thử cái này :

//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); }
        }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.