web-dev-qa-db-ja.com

TimeSpanをXMLにシリアル化する方法

.NET TimeSpanオブジェクトをXMLにシリアル化しようとしていますが、動作していません。簡単なグーグルは、TimeSpanはシリアライズ可能ですが、XmlCustomFormatterTimeSpanオブジェクトとXMLを相互に変換するメソッドを提供しないことを示唆しています。

推奨されるアプローチの1つは、シリアル化のために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をシリアル化するより良い方法はありますか?

199
joeldixon66

すでに投稿した方法がおそらく最もクリーンです。余分なプロパティが気に入らない場合は、IXmlSerializableを実装できますが、everythingを実行する必要があり、これによりポイントが大幅に無効になります。あなたが投稿したアプローチを喜んで使用します。 (たとえば)効率的(複雑な解析などなし)で、文化に依存せず、明確で、タイムスタンプ型の数値は簡単かつ一般的に理解されています。

余談として、私はしばしば追加します:

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

これは、混乱を避けるために、UIおよびDLLの参照でそれを非表示にします。

69
Marc Gravell

これは、質問で提案されたアプローチをわずかに変更しただけですが、 この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はTimeSpanをサポートします。

97
Rory MacLeod

場合によっては、パブリックプロパティにバッキングフィールド(TimeSpan)を与えることができますが、パブリックプロパティは文字列として公開されます。

例えば:

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

プロパティ値の大部分が包含クラスまたは継承クラスで使用され、xml構成からロードされる場合、これは問題ありません。

パブリックプロパティを他のクラスで使用可能なTimeSpan値にしたい場合、他の提案されたソリューションの方が優れています。

28
Wes

色のシリアル化この元のソリューション (それ自体は素晴らしい)からの回答を組み合わせて、私はこのソリューションを得ました:

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

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>
9
phoog

より読みやすいオプションは、文字列としてシリアル化し、_TimeSpan.Parse_メソッドを使用して逆シリアル化することです。例と同じようにできますが、ゲッターでTimeSpan.ToString()を使用し、セッターでTimeSpan.Parse(value)を使用します。

8
Rune Grimstad

Timespanはxmlに秒数として保存されますが、簡単に採用できると思います。手動でシリアル化されたタイムスパン(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));
            }
       }
    }
}

より包括的な例があります: https://bitbucket.org/njkazakov/timespan-serialization

Settings.csを見てください。また、XmlElementAttributeを使用するためのトリッキーなコードがいくつかあります。

1
askazakov

ソリューションの私のバージョン:)

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

編集:それがnull可能だと仮定して...

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

データコントラクトのシリアル化には、次を使用します。

  • シリアル化されたプロパティをプライベートにすると、パブリックインターフェイスがクリーンになります。
  • シリアル化にパブリックプロパティ名を使用すると、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
0
JRS

回避策が必要ない場合は、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);
        }
0
Sasha Yakobchuk