.NET TimeSpan
オブジェクトをXMLにシリアル化しようとしていますが、動作していません。簡単なグーグルは、TimeSpan
はシリアライズ可能ですが、XmlCustomFormatter
はTimeSpan
オブジェクトと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をシリアル化するより良い方法はありますか?
すでに投稿した方法がおそらく最もクリーンです。余分なプロパティが気に入らない場合は、IXmlSerializable
を実装できますが、everythingを実行する必要があり、これによりポイントが大幅に無効になります。あなたが投稿したアプローチを喜んで使用します。 (たとえば)効率的(複雑な解析などなし)で、文化に依存せず、明確で、タイムスタンプ型の数値は簡単かつ一般的に理解されています。
余談として、私はしばしば追加します:
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
これは、混乱を避けるために、UIおよびDLLの参照でそれを非表示にします。
これは、質問で提案されたアプローチをわずかに変更しただけですが、 この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をサポートします。
場合によっては、パブリックプロパティにバッキングフィールド(TimeSpan)を与えることができますが、パブリックプロパティは文字列として公開されます。
例えば:
protected TimeSpan myTimeout;
public string MyTimeout
{
get { return myTimeout.ToString(); }
set { myTimeout = TimeSpan.Parse(value); }
}
プロパティ値の大部分が包含クラスまたは継承クラスで使用され、xml構成からロードされる場合、これは問題ありません。
パブリックプロパティを他のクラスで使用可能なTimeSpan値にしたい場合、他の提案されたソリューションの方が優れています。
色のシリアル化 と この元のソリューション (それ自体は素晴らしい)からの回答を組み合わせて、私はこのソリューションを得ました:
[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); }
}
}
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>
より読みやすいオプションは、文字列としてシリアル化し、_TimeSpan.Parse
_メソッドを使用して逆シリアル化することです。例と同じようにできますが、ゲッターでTimeSpan.ToString()
を使用し、セッターでTimeSpan.Parse(value)
を使用します。
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を使用するためのトリッキーなコードがいくつかあります。
ソリューションの私のバージョン:)
[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;
}
}
データコントラクトのシリアル化には、次を使用します。
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
回避策が必要ない場合は、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);
}