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


206

.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との間でシリアル化するより良い方法はありますか?


4
以下のRory MacLeodの答えは、実際にはMicrosoftがこれを行うことを推奨する方法です。
Jeff

2
XMLの期間タイプは完全一致であるため、TimeSpandに長いティックを使用しません。この問題は2008年にマイクロソフトに提起されましたが、解決されませんでした。当時、文書化の回避策があります:kennethxu.blogspot.com/2008/09/...
ケネス・徐

回答:


71

あなたが既に投稿した方法はおそらく最もきれいです。追加のプロパティが気に入らない場合は、を実装できますがIXmlSerializable、その後、すべてを実行する必要があります。私はあなたが投稿したアプローチを喜んで使用します。(たとえば)効率的で(複雑な解析などは不要)、カルチャに依存せず、明確で、タイムスタンプタイプの数値は簡単に一般的に理解されます。

余談ですが、私はよく付け加えます:

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

これは、混乱を避けるために、UIとDLLの参照で非表示にするだけです。


5
MyClassに実装するのではなく、System.TimeSpanをラップする構造体にインターフェースを実装する場合、すべてを行うことはそれほど悪くありません。次に、MyClass.TimeSinceLastEventプロパティのタイプを変更するだけです
phoog

103

これは質問で提案されているアプローチのわずかな変更にすぎませんが、この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をサポートしています。


15
XmlConvert.ToTimeSpan()の+1。それはPT2H15MのようなタイムスパンのためのISO標準期間構文を処理し、参照en.wikipedia.org/wiki/ISO_8601#Durations
yzorg

2
私が間違っている場合は修正してください。ただし、セルライズされたTimeSpan "PT2M45S"は、2:45:00ではなく00:02:45です。
TomPažourek2013

接続リンクが壊れています。おそらく次のリンクに置き換えることができます:connect.microsoft.com/VisualStudio/feedback/details/684819/…?テクニックも少し異なります...
TJB

フォローアップする奇妙な質問ですが、この値PT2M45SをSQLのTimeに逆シリアル化する方法はありますか?
Xander

28

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

例えば:

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

これは、プロパティ値が主に包含クラスまたは継承クラス内で使用され、xml構成から読み込まれる場合は問題ありません。

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


はるかに簡単なソリューションです。私はまったく同じものを思いついたのですが、それは魅力のように機能します。実装と理解が簡単です。
wpfwannabe 2011

1
これがここでの最良のソリューションです。とてもいい連載です!!! 友達入力ありがとうございます!
開発者

25

Colorシリアライゼーションからの回答とこのオリジナルのソリューション(それ自体は素晴らしい)を組み合わせて、私はこのソリューションを得ました。

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

この問題を解決する最善かつ簡単な方法...私にとって
Moraru Viorel

これは完全に独創的です-私はとても感動しました!
ジム

9

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>

XmlAttributeとして出力を作成する方法はありますか?
ALA

@ala、私があなたの質問を正しく理解している場合、答えは、XmlAttributeAttributeを属性として表現したいプロパティに適用することです。もちろん、これはTimeSpanに固有のものではありません。
phoog

+1いいですねTicks。ただし、それを文字列としてシリアル化するのではなく、できるだけ長くします。
ChrisWue 2013年

@ChrisWue私のオフィスでは、人間が読み取れる出力が必要なときにxmlシリアル化を使用しています。タイムスパンをlongとしてシリアル化することは、その目標と完全に互換性がありません。もちろん、別の理由でxmlシリアル化を使用する場合は、目盛りをシリアル化する方が理にかなっています。
phoog 2013年

8

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


2

別のオプションは、SoapFormatterクラスではなくクラスを使用してシリアル化することXmlSerializerです。

結果のXMLファイルは少し異なって見えます...「SOAP」が前に付けられたタグなど...しかし、それは可能です。

以下SoapFormatterは、20時間28分のタイムスパンをシリアル化したものです。

<myTimeSpan>P0Y0M0DT20H28M0S</myTimeSpan>

SOAPFormatterクラスを使用するにSystem.Runtime.Serialization.Formatters.Soapは、同じ名前のネームスペースへの参照を追加して使用する必要があります。


これは.net 4.0でシリアル化する方法です
Kirk Broadhurst

1

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

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

編集:それが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

タイムスパンは秒数として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
そのリンクから関連情報を引用してください。回答に必要なすべての情報はこのサイトにある必要があります。そうすれば、そのリンクをソースとして引用できます。
SuperBiasedMan、2015年

0

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

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

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

-2

これを試して :

//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); }
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.