Möchte man einen TimeSpan mittels XmlSerializer serialisieren, merkt man schnell, dass dies nicht richtig zu funktionieren scheint. An dieser Stelle möchte ich einen unkomplizierten Workaround zeigen, mit dem TimeSpan-Werte problemlos mit dem XmlSerializer serialisiert werden können.
Das Problem
Betrachten wir zunächst folgende Klasse, die serialisiert werden soll:
public class SerializableClass { public string MyString { get; set; } public TimeSpan MyTimeSpan { get; set; } }
Diese Klasse bietet lediglich zwei Eigenschaften, die einen String und einen TimeSpan erwarten.
Der folgende Code zeigt das Serialisieren der SerializableClass:
var fileName = "MyData.xml"; var data = new SerializableClass(); data.MyString = "Test"; data.MyTimeSpan = TimeSpan.FromSeconds(10); if (File.Exists(fileName)) File.Delete(fileName); using (FileStream stream = File.Open("MyData.xml", FileMode.CreateNew)) { var serializer = new XmlSerializer(typeof(SerializableClass)); serializer.Serialize(stream, data); }
Wirft man nun einen Blick in die resultierende XML-Datei, stellt man fest, dass der TimeSpan-Wert (in diesem Fall 10 Sekunden) nicht korrekt serialisiert wurde und einen leeren Wert anzeigt, der String wurde allerdings korrekt serialisiert:
<?xml version="1.0"?> <SerializableClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <MyString>Test</MyString> <MyTimeSpan /> </SerializableClass>
Die Lösung
Um dennoch eine Serialisierung per XmlSerializer zu bewerkstelligen, ist eine Änderung an der zu serialisierenden Klasse erforderlich:
public class SerializableClass { public string MyString { get; set; } [XmlIgnore] public TimeSpan MyTimeSpan { get; set; } [XmlElement("MyTimeSpan")] public long MyTimeSpanSubstitute { get { return this.MyTimeSpan.Ticks; } set { this.MyTimeSpan = TimeSpan.FromTicks(value); } } }
Die ursprüngliche Eigenschaft MyTimeSpan wurde mit dem Attribut XmlIgnore gekennzeichnet. Dadurch wird diese Eigenschaft (oder besser gesagt der dadurch repräsentierte Wert) nicht von der XML-Serialisierung erfasst. Somit würde der Wert von MyTimeSpan nicht in der resultierenden XML-Datei zu finden sein.
Damit der Wert aber trotzdem von der XML-Serialisierung erfasst wird, wurde das Property MyTimeSpanSubstitute hinzugefügt. Durch das Attribut XmlElement(„MyTimeSpan“) wird dafür gesorgt, dass in der resultierenden XML-Datei der TimeSpan-Wert im XML-Element MyTimeSpan gespeichert wird (man beachte, dass das XML-Element den gleichen Namen hat wie die Eigenschaft, die zuvor von der XML-Serialisierung ausgeschlossen wurde).
In der Eigenschaft MyTimeSpanSubstitute wird nun der Wert des MyTimeSpan-Properties gelesen und gesetzt. Da ein TimeSpan intern eine Zeitspanne mittels Ticks (1 Tick = 100 Nanosekunden) repräsentiert, wird der TimeSpan-Wert hier auf Grund der Ticks gelesen oder gesetzt.
Hier die resultierende XML-Datei, in dieser wird nun der TimeSpan korrekt serialisiert:
<?xml version="1.0"?> <SerializableClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <MyString>Test</MyString> <MyTimeSpan>100000000</MyTimeSpan> </SerializableClass>
Der einzige Nachteil bei der ganzen Sache ist, dass man in der zu serialisierenden Klasse eine weitere (public) Eigenschaft hat. Auch sollte man Verwender des Codes darauf hinweisen, dass das Property MyTimeSpanSubsitute nur für den internen Gebrauch gedacht ist und ansonsten nicht zu verwenden ist.
Hintergründe
Warum die XML-Serialisierung eines TimeSpan-Wertes nicht wie erwartet funktioniert, werden wohl nur die Programmierer von Microsoft wissen. Bei der binären Serialisierung mittels BinaryFormatter funktioniert das Ganze erwartungskonform. Hier ist ein solcher Workaround nicht notwendig.