TimeSpan'ı XML'ye serileştirme


206

TimeSpanXML için bir .NET nesnesi serileştirmek çalışıyorum ve çalışmıyor. Hızlı bir Google TimeSpan, serileştirilebilir olsa da, XML'e ve XML'den nesne XmlCustomFormatterdönüştürmek için yöntemler sağlamadığını önerdi TimeSpan.

Önerilen bir yaklaşım, TimeSpanserileştirmeyi göz ardı etmek ve bunun yerine sonucunu serileştirmek TimeSpan.Ticks(ve serileştirme için kullanmak new TimeSpan(ticks)) idi. Bunun bir örneği:

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

Bu benim kısa testimde işe yarıyor gibi görünse de, bunu başarmanın en iyi yolu bu mu?

XML'e ve XML'den bir TimeSpan serileştirmenin daha iyi bir yolu var mı?


4
Rory MacLeod'un aşağıdaki cevabı aslında Microsoft'un bunu önerdiği yoldur.
Jeff

2
XML'in süre türü tam olarak eşleştiğinden TimeSpand için uzun keneler kullanmazdım. Sorun 2008 yılında Microsoft'a getirildi, ancak hiç çözülmedi. O zamanlar belgelenen bir geçici çözüm var: kennethxu.blogspot.com/2008/09/…
Kenneth Xu

Yanıtlar:


71

Daha önce yayınladığınız yol muhtemelen en temiz olanıdır. Ekstra mülkü beğenmezseniz, uygulayabilirsiniz IXmlSerializable, ancak o zaman her şeyi yapmanız gerekir , ki bu da büyük ölçüde noktayı yener. Gönderdiğiniz yaklaşımı memnuniyetle kullanırdım; (örneğin) etkilidir (karmaşık ayrıştırma vb. olmadan), kültürden bağımsız, açık ve zaman damgası tipi sayılar kolayca ve yaygın olarak anlaşılır.

Bir yana, sık sık ekliyorum:

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

Bu sadece karışıklığı önlemek için, kullanıcı arayüzünde ve referans dll'lerde gizler.


5
Doing her şeyi Bunun yerine Sınıfım belirlenmesi ve uygulanması yerine, System.TimeSpan sarar bir yapı üzerinde arabirimini uygulamak eğer o kadar kötü değil. O zaman sadece MyClass.TimeSinceLastEvent özelliğinizin türünü değiştirmeniz gerekir
phoog

103

Bu, soruda önerilen yaklaşımda sadece küçük bir değişikliktir, ancak bu Microsoft Connect sorunu, aşağıdaki gibi serileştirme için bir özellik kullanılmasını önerir:

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

Bu, 0:02:45 zaman dilimini şu şekilde serileştirir:

<TimeSinceLastEvent>PT2M45S</TimeSinceLastEvent>

Alternatif olarak, DataContractSerializerTimeSpan'ı destekler.


15
XmlConvert.ToTimeSpan () için +1. Bu PT2H15M gibi zaman için ISO standardı süresi sözdizimi kolları, bkz en.wikipedia.org/wiki/ISO_8601#Durations
yzorg

2
Eğer yanılıyorsam beni düzeltin ama serlize TimeSpan "PT2M45S" 00:02:45, 2:45:00 değil.
Tom Pažourek

Bağlantı bağlantısı kesildi, belki bununla değiştirilebilir: connect.microsoft.com/VisualStudio/feedback/details/684819/… ? Teknik de biraz farklı görünüyor ...
TJB

Takip etmek için garip bir soru, bu değeri PT2M45S to Time SQL'de seriden kaldırmak için bir yol var mı?
Xander

28

Bazı durumlarda işe yarayabilecek bir şey, kamu mülkünüze bir TimeSpan olan bir destek alanı vermektir, ancak kamu mülkü bir dize olarak gösterilir.

Örneğin:

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

Özellik değeri çoğunlukla içeren sınıfta veya devralma sınıflarında w / kullanılırsa ve xml yapılandırmasından yüklenmişse bu sorun olmaz.

Ortak mülkün diğer sınıflar için kullanılabilir bir TimeSpan değeri olmasını istiyorsanız, önerilen diğer çözümler daha iyidir.


Şimdiye kadar en kolay çözüm. Aynı şeyi buldum ve bir cazibe gibi çalışıyor. Uygulaması ve anlaşılması kolaydır.
wpfwannabe

1
Buradaki en iyi çözüm bu. Çok iyi serileştirir !!! Giriş arkadaşınız için teşekkürler!
Geliştirici

25

Color serialization ve bu orijinal çözümü (ki kendi başına harika) bir cevap birleştirerek bu çözümü aldım:

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

burada XmlTimeSpansınıf şöyle:

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

Bu sorunu çözmenin en iyi ve kolay yolu ... benim için
Moraru Viorel

Bu kesinlikle ustaca - süper etkilendim!
Jim

9

TimeSpan yapısının etrafında hafif bir sargı oluşturabilirsiniz:

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

Örnek serileştirilmiş sonuç:

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

nasıl XmlAttribute çıktı yapmak için herhangi bir fikir?
ala

@ala, Sorunuzu doğru anlarsam, yanıt, XmlAttributeAttribute öğesini öznitelik olarak ifade etmek istediğiniz özelliğe uygulamaktır. Bu elbette TimeSpan için özel değil.
phoog

+1 Güzel, ben dize olarak değil Ticksuzun sürece seri hale getirmek dışında .
ChrisWue

@ChrisWue Ofisimde insan tarafından okunabilir çıktı istediğimizde xml serileştirme kullanıyoruz; bir zaman aralığını uzun süre serileştirmek bu hedefle pek uyumlu değildir. Farklı bir nedenden dolayı xml serileştirme kullanırsanız, elbette, kenelerin serileştirilmesi daha mantıklı olabilir.
phoog

8

Daha okunabilir bir seçenek, bir dize olarak serileştirmek ve dizinin TimeSpan.Parseserisini kaldırmak için yöntemi kullanmak olacaktır. Örneğinizle aynı şeyi yapabilirsiniz, ancak TimeSpan.ToString()alıcıda ve TimeSpan.Parse(value)ayarlayıcıda kullanabilirsiniz.


2

Başka bir seçenek de SoapFormattersınıf yerine sınıfı kullanarak serileştirmektir XmlSerializer.

Ortaya çıkan XML dosyası biraz farklı görünüyor ... bazı "SOAP" - önceden düzeltilmiş etiketler, vb ... ama bunu yapabilir.

İşte SoapFormatter20 saatlik ve 28 dakikalık bir zaman aralığını serileştiren:

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

SOAPFormatter sınıfını System.Runtime.Serialization.Formatters.Soapkullanmak için, aynı adın başvurusunu eklemeniz ve ad alanını kullanmanız gerekir .


.Net 4.0
Kirk Broadhurst

1

Çözümümün sürümü :)

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

Düzenleme: sıfırlanabilir olduğu varsayılarak ...

[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

Zaman aralığı xml'de saniye sayısı olarak saklanır, ancak benimsenmesi kolaydır, umarım. Manuel olarak serileştirilmiş zaman aralığı (IXmlSerializable uygulanıyor):

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

Daha kapsamlı bir örnek var: https://bitbucket.org/njkazakov/timespan-serialization

Settings.cs dosyasına bakın. Ve XmlElementAttribute kullanmak için bazı zor kodlar var.


1
Lütfen bu bağlantıdan ilgili bilgileri verin. Cevabınız için gereken tüm bilgiler bu sitede olmalıdır ve daha sonra bu bağlantıyı kaynak olarak gösterebilirsiniz.
SuperBiasedMan

0

Veri sözleşmesi serileştirme için aşağıdakileri kullanıyorum.

  • Serileştirilmiş özelliği gizli tutmak, genel arayüzü temiz tutar.
  • Serileştirme için ortak özellik adını kullanmak XML'yi temiz tutar.
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

Herhangi bir geçici çözüm istemiyorsanız, System.Runtime.Serialization.dll dosyasından DataContractSerializer sınıfını kullanın.

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

-2

Bunu dene :

//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); }
        }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.