Für eine kleine Auftragsarbeit im Familienkreis will ich mit Silverlight 5 eine XML Datei lesen und schreiben – als Ersatz für den Schedule Wizzard des Bus Simulator 2012 Spiels, der unter Windows XP nicht richtig funktioniert. Wenn man allerdings versucht, eine lokale Datenstrom in ein XDocument zu laden oder ein solches zu schreiben, so gibt es eine kleine Überraschung: von Hause aus unterstützt Silverlight das Windows Encoding und esp. die 8-Bit Windows Codepage 1252 nicht.
Tatsächlich sind die hier verwendeten Dateien genau betrachtet auch unzulässig, allerdings drücke ich doch allzu gerne beide Auge zu, da ich selbst schon oft in diese Falle gelaufen bin. Hier werden XML Dateien verwendet, in deren ?xml Instruktion kein Encoding angegeben ist, die aber tatsächlich in der 8-Bit Codepage 1252 von Windows verfasst sind. Das heißt zum Beispiel, dass das uro Symbol mit dem Wert 128 hinterlegt ist – entsprechend auch die Umlaute. Silverlight wird eine solche Datei mit 1252er Umlauten erst gar nicht laden und beim Speichern das UTF-8 Format verwenden – inklusive einem korrekten Encoding in der ?xml Instruktion. Damit kommt aber das Spiel nicht zurrecht.
Ich habe ohne langes Suchen eine kleine eigene Implementierung verwendet, die ich hier kurz vorstellen will – recht trivial und meiner Ansicht nach dem Zweck angemessen. Die Implementierung selbst findet man in diesem Archiv.
Erst einmal habe ich mir (mit Hilfe vom richtigen .NET und dem Encoding.GetEncoding(1252)) eine Abbildungsvorschrift zwischen Windows 1252 Bytes und 16-Bit UNICODE Zeichen erstellt und diese in Silverlight eingebunden (man siehe hierzu Fahrplan.cs im Archiv). Beim Laden wird die gesamte Datei (die für die konkrete Anwendung extrem klein ist) binär als byte-Feld unverändert eingelesen. Mit Hilfe der Abbildungsvorschrift wird pro byte das zugehörige UNICODE Zeichen als char erstellt, i.e. 128 gibt und so weiter. Dieses char-Feld bildet dann eine Zeichenkette via new string(feld) und über die System.IO.StringReader Klasse kann das XDocument klaglos erstellt werden.
Das Speichern ist ein wenig trickreicher. Hier wird das XDocument erst einmal im Speicher mit Hilfe einer MemoryStream Instanz gespeichert. Der Einsatz von XmlWriter und XmlWriterSettings erlaubt es dabei, das Encoding auf Encoding.Unicode zu fixieren. Die UNICODE char Zeichen im Speicher werden nun alle über die Abbildungsvorschrift in Windows 1252 Bytes umgesetzt – ist dies nicht möglich, wird ein ? als Ersetzungszeichen verwendet. Der erste char wird dabei ignoriert, hier würde in der Datei die BOM (Byte-Order-Mark) stehen, die eine Identifikation des Dateiinhaltes als UNICODE ermöglichen soll. Somit wird aus einem ursprünglichen UNICODE char-Feld einfach ein byte-Feld in Windows 1252 Codierung, das dann binär in eine lokale Datei gespeichert wird. Aber Vorsicht: hier ist in der ?xml Instruktion immer noch das Encoding als UTF-16 festgelegt, was zumindest von der Originalformatierung abweicht. Im Programmcode werden die entsprechenden Bytes beim Schreiben der Datei entfernt.
Etwas ekelig, aber nicht so schlimm, wie es sich anhört. Der eigentliche Code zum Lesen und Schreiben sind wenige Zeile und die lange Abbildungsvorschrift kann man sich leicht automatisiert erstellen. Besser als ein eigenes Encoding, oder?
Soweit zum Thema: Was Sie eigentlich nie über Silverlight wissen wollten aber gezwungen waren sich damit auseinander zu setzen 🙂
Jochen