2914 Blogs online | 28 Blogs warten auf Prüfung |6022 Mitglieder | 886162 Klicks | 168 Kommentare | 0 Mitglieder, 28 Gäste online


Blogkategorien
Neue Kategorie vorschlagen
RSS FEED
Zufallsblog
Deutschlandpolitik
Deutsche Politik, Gesellschaft und Geschichte: Nachrichten, Satire und ein Blick hinter die Kulissen

klaus_b@.NET | Das private Weblog von Klaus Bock über alles was an .NET und C# Spass macht.

Blog-Beschreibung: Das private Weblog von Klaus Bock über alles was an .NET und C# Spass macht. Hier wird von ASP.NET über PInvoke und WIN32 bis XML Web Services über eine breite Palette des .NET Framework geschrieben.
Eingetragen am: 04.03.2009 - 10:42:08 (Geändert 26.01.2010 - 22:07:19)
Bewertung: 5
5/5 basierend auf 1 Bewertungen
Kommentare: 0 Kommentare
beigefügte Anlagen:
BlogBanner.png (2 KB, 49 Downloads)
klaus_b@.NET | Das private Weblog von Klaus Bock über alles was an .NET und C# Spass macht.
RSS Feed Daten:

Binär, XML und JSON vs. Custom-Serializer von am Tue, 03 Jan 2012 14:22:30 +0100:
<p><img class="leftImg" title="binärer Code" alt="binär code" src="http://blog.klaus-b.net/image.axd?picture=binary.png" img="img" />… oder; wenn du schnell sein willst musst du dich selbst darum kümmern. <br />Im <a title="Binär, XML und JSON-Serialisierung im Vergleich" href="http://blog.klaus-b.net/post/2011/12/30/Binar-XML-und-JSON-Serialisierung-im-Vergleich.aspx">vorherigen Artikel</a> wurden bisher nur Standardserialiserer und ihre Formate miteinander verglichen. Die Ergebnisse waren schon recht ordentlich und die Erkenntnisse aus den Vergleichen sehr lehrreich. Vor allem der Vergleich <a title="Extensible Markup Language" href="http://de.wikipedia.org/wiki/XML" rel="wiki nofollow">XML</a> vs. <a title="JavaScript Object Notation" href="http://de.wikipedia.org/wiki/JSON" rel="wiki nofollow">JSON</a> animierte mich dazu, die gewonnenen Erkenntnisse weiter zu führen. Meines Erachtens nach, resultiert der Vorteil von JSON gegenüber XML in der deutlich schlankeren Definition der erzeugten Daten. Während XML vom Klassennamen über die Namen der Enthaltenen Eigenschaften bis zum Typ des Inhalts von Kollektionen alles schreibt, beschränkt sich JSON auf den Namen der Eigenschaften und ihren Inhalt. Wenn ich diesen Gedankengang konsequent weiterführe, kann ich in einem angepassten Serialisierer selbst auf die Namen der Eigenschaften verzichten und statt dessen mittels <a title="Indexer (C#)" href="http://msdn.microsoft.com/6x16t2tx.aspx" rel="msdn nofollow">Indexer</a> auf den jeweiligen Wert des serialisierten Objekts zugreifen. <br />Als nächste Konsequenz kann die komplette Typbehandlung beim De/Serialisieren entfallen, da der zu de/serialiserende Typ bekannt ist und nur dieser verwendet wird. Aber nun der Reihe nach.</p><p>Der Anlass für die Vergleiche der verschiedenen Serialisierer, war eine Anforderung aus einem aktuellen Projekt über das ich in <a title="Datensammlungen über HTTP serialisieren" href="http://blog.klaus-b.net/post/2011/12/27/Datensammlungen-uber-HTTP-serialisieren.aspx">diesem Artikel</a> bereits geschrieben haben: <br />“<em>Eine Kollektion eines bekannten Typs in einem geschlossenen System schnell und effizient zu serialisieren und deserialisieren.</em>” <br />Solange ich einen der Standardserialisierer verwende, werde ich immer die Typbehandlung des jeweiligen Serialisierer in Kauf nehmen müssen, da der Serialisierer mit beinahe jedem gängigen .NET Objekt umgehen muss. Warum also nicht die Serialisierung auf das notwendige beschränken? Das Notwendige ist per Definition alleinig der Inhalt der öffentlichen Eigenschaften.</p><p>Wie könnte so eine serialisierte Zeichenfolge aussehen, die den Inhalt der öffentlichen Eigenschaften darstellt? <br />Da eine Kollektion serialisiert werden soll, muss als erstes zwischen den einzelnen Einträgen der Auflistung getrennt werden. Als nächstes muss eine Trennung zwischen den einzelnen Eigenschaften eines Eintrags her. Am einfachsten erschien mir ein Muster nach folgendem Beispiel: <br /><strong>[#|#|#…]</strong><br />Die eckigen Klammern umschließen eine Klasse, also einen Eintrag in der Auflistung. Mit einem oder-Operator (<strong>|</strong>) wird zwischen den einzelnen Eigenschaften abgegrenzt. Zur Darstellung eines <a title="Array Klasse" href="http://msdn.microsoft.com/czz5hkty.aspx" rel="msdn nofollow">Array</a> oder einer anderen Auflistung habe ich mich für folgendes Muster entschieden: <br /><strong>(n,n,n…)</strong><br />Kombiniert ergibt sich für die Klasse <em>TestNode</em>, aus <a title="Datensammlungen über HTTP serialisieren" href="http://blog.klaus-b.net/post/2011/12/27/Datensammlungen-uber-HTTP-serialisieren.aspx">diesem Artikel</a>, das endgültige Muster: <br /><strong>[#|#|#|(n,n,n…)]</strong></p><p>Zum erzeugen der Zeichenfolge, habe ich mich für eine Überladung der <a title="Object.ToString Methode" href="http://msdn.microsoft.com/library/7bxwbwt2.aspx" rel="msdn nofollow">ToString</a>-Methode entschlossen, die eine Zeichenfolge als Format erwartet.</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:7e456edc-6f0b-4a7c-9d6c-8bc9711d919b" class="wlWriterEditableSmartContent"><pre name="code" class="c#:firstline[46]">internal string ToString(string format) { return string.Format( CultureInfo.InvariantCulture, "[{0}|{1}|{2}|({3})]", this.Name, this.MessagingUrl, this.Token, string.Join( ",", (this.Ranges .Select(i => i.ToString(CultureInfo.InvariantCulture))) .ToArray())); }</pre></div><p>Für diesen Benchmark wird der Parameter <em>format</em> noch nicht ausgewertet. Später könnten hier Werte wie etwa “C” für Custom oder “J” für JSON angegeben und entsprechend verarbeitet werden.</p><p>Die eigentliche Serialisierung in einem <em>CustomFormatter</em> ist eher trivial. Es wird lediglich die Auflistung durchlaufen, für jedes Element die überladene ToString(<a title="String Klasse" href="http://msdn.microsoft.com/s1wwdcbf.aspx" rel="msdn nofollow">string</a>)-Methode aufgerufen und die zurückgegebene Zeichenfolge in einen <a title="StreamWriter Klasse" href="http://msdn.microsoft.com/library/3ssew6tk.aspx">StreamWriter</a> geschrieben.</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:2f625958-4fdd-48c8-a4b1-b4695c123211" class="wlWriterEditableSmartContent"><pre name="code" class="c#:firstline[12]">public void Serialize(HttpResponse response, List<TestNode> data) { response.ContentType = "application/json"; using (var writer = new StreamWriter(response.OutputStream)) { foreach (var entry in data) { writer.Write(entry.ToString(null)); } } }</pre></div><p>Die Deserialisierung ist ähnlich einfach. Der empfangene <a title="Stream Klasse" href="http://msdn.microsoft.com/8f86tw9e.aspx" rel="msdn nofollow">Stream</a> wird in einen <a title="StreamReader Klasse" href="http://msdn.microsoft.com/6aetdk20.aspx" rel="msdn nofollow">StreamReader</a> gelesen und die enthaltene Zeichenfolge, dem vorher beschriebenen Muster entsprechend, aufgeteilt und die erzeugten Fragmente jeweils einer neuen Instanz der Klasse <em>TestNode</em> bzw. den Eigenschaften zugewiesen.</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:7808fb75-5bb7-46e7-a698-4dd119874ecc" class="wlWriterEditableSmartContent"><pre name="code" class="c#:firstline[30]">public List<TestNode> Deserialize(Stream responseStream) { var list = new List<TestNode>(); var rawSeparator = new char[] { '[', ']' }; var propertySeparator = new char[] { '|' }; var rangeSeparator = new char[] { '(', ')', ',' }; using (var reader = new StreamReader(responseStream)) { var rawData = reader.ReadToEnd() .Split(rawSeparator, StringSplitOptions.RemoveEmptyEntries); foreach (var entry in rawData) { var properties = entry.Split( propertySeparator, StringSplitOptions.RemoveEmptyEntries); if (properties.Length < 1) { continue; } var ranges = properties[3] .Split(rangeSeparator, StringSplitOptions.RemoveEmptyEntries) .Select(s => int.Parse(s, CultureInfo.InvariantCulture)); var node = new TestNode { Name = properties[0], MessagingUrl = properties[1], Token = properties[2], Ranges = new List<int>(ranges) }; list.Add(node); } } return list; }</pre></div><p>Die Erzeugung der temporären Variablen <em>ranges</em>, ist nur der Übersicht halber im Code enthalten. Das Aufteilen und parsen der Zeichenfolge kann auch im Konstruktor von <a title="List(T) Klasse" href="http://msdn.microsoft.com/library/6sh2ey19.aspx" rel="msdn nofollow">List<T></a> für die Eigenschaft <em>Ranges</em> erfolgen.</p><p>Nach dem die Methodik stand wollte ich natürlich wissen, ob auch der erwartete Effekt eintrat bzw. eine verbesserte Leitung gegenüber den bisher verwendeten Serialisierern messbar ist.</p><p>Der Benchmark wurde um die Messung der Deserialisierung erweitert. So kann ein besseres Gesamtbild des jeweiligen Serialisierers wiedergegeben werden. <br />Die Werte der Spalten <em>Response</em>, <em>Deseria</em>. und <em>Complete</em> sind jeweils in Millisekunden dargestellt. <em>Count</em> gibt die Anzahl der Elemente in der übertragenen Auflistung wieder und <em>Transfered</em> die Anzahl der übertragenen Bytes.</p><p>Ich habe kurzerhand die beschriebene Methodik als <em>CustomFormatter</em> mit dem Kürzel <em>custom</em> dem Benchmark hinzugefügt und dieses laufen lassen. <br />Was soll ich sagen; die Zahlen sprechen für sich.</p><table border="1" cellspacing="0" cellpadding="2" width="536"><tbody><tr><td width="70" align="right"><strong>Format</strong></td><td valign="top" width="63" align="center"><strong>Count</strong></td><td valign="bottom" width="63" align="center"><strong>Comp</strong></td><td valign="top" width="85" align="center"><strong>Response</strong></td><td valign="top" width="76" align="center"><strong>Deseria</strong>.</td><td valign="top" width="85" align="center"><strong>Complete</strong></td><td valign="top" width="92" align="right"><strong>Transfered</strong></td></tr><tr><td width="70" align="right">bin</td><td valign="bottom" width="63" align="right">1 <br />1</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">1 <br /></td><td valign="bottom" width="76" align="right"><br />1</td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="92" align="right">968 <br />559</td></tr><tr><td width="70" align="right">xml</td><td valign="top" width="63" align="right">1 <br />1</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">1</td><td valign="top" width="76"> </td><td valign="top" width="85" align="right">1</td><td valign="top" width="92" align="right">423 <br />364</td></tr><tr><td width="70" align="right">jsondata</td><td valign="top" width="63" align="right">1 <br />1</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">1</td><td valign="top" width="76"> </td><td valign="top" width="85" align="right">1</td><td valign="top" width="92" align="right">219 <br />269</td></tr><tr><td width="70" align="right">jsonweb</td><td valign="top" width="63" align="right">1 <br />1</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">2</td><td valign="top" width="76"> </td><td valign="top" width="85" align="right">2</td><td valign="top" width="92" align="right">148 <br />246</td></tr><tr><td width="70" align="right">jsonnet</td><td valign="top" width="63" align="right">1 <br />1</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">2 <br />5</td><td valign="top" width="76"> </td><td valign="top" width="85" align="right">2 <br />5</td><td valign="top" width="92" align="right">148 <br />246</td></tr><tr><td width="70" align="right">custom</td><td valign="top" width="63" align="right">1 <br />1</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right"><br />1</td><td valign="top" width="76"> </td><td valign="top" width="85" align="right"><br />1</td><td valign="top" width="92" align="right">101 <br />209</td></tr></tbody></table><table border="1" cellspacing="0" cellpadding="2" width="535"><tbody><tr><td width="71" align="right"><strong>Format</strong></td><td valign="top" width="62" align="center"><strong>Count</strong></td><td valign="bottom" width="62" align="center"><strong>Comp</strong></td><td valign="top" width="85" align="center"><strong>Response</strong></td><td valign="top" width="76" align="center"><strong>Deseria</strong>.</td><td valign="top" width="85" align="center"><strong>Complete</strong></td><td valign="top" width="92" align="right"><strong>Transfered</strong></td></tr><tr><td width="71" align="right">bin</td><td valign="bottom" width="62" align="right">10 <br />10</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="76" align="right"> </td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="92" align="right">2.581 <br />1.372</td></tr><tr><td width="71" align="right">xml</td><td valign="top" width="62" align="right">10 <br />10</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">4 <br />1</td><td valign="top" width="76" align="right">1</td><td valign="top" width="85" align="right">1</td><td valign="top" width="92" align="right">3.494 <br />947</td></tr><tr><td width="71" align="right">jsondata</td><td valign="top" width="62" align="right">10 <br />10</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="76" align="right"> </td><td valign="top" width="85" align="right">1</td><td valign="top" width="92" align="right">2.300 <br />813</td></tr><tr><td width="71" align="right">jsonweb</td><td valign="top" width="62" align="right">10 <br />10</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="76" align="right"> </td><td valign="top" width="85" align="right">2</td><td valign="top" width="92" align="right">1.590 <br />773</td></tr><tr><td width="71" align="right">jsonnet</td><td valign="top" width="62" align="right">10 <br />10</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">2 <br /></td><td valign="top" width="76" align="right">1 <br />1</td><td valign="top" width="85" align="right">3 <br />1</td><td valign="top" width="92" align="right">1.590 <br />773</td></tr><tr><td width="71" align="right">custom</td><td valign="top" width="62" align="right">10 <br />10</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">2</td><td valign="top" width="76" align="right"> </td><td valign="top" width="85" align="right">2 <br /></td><td valign="top" width="92" align="right">1.129 <br />723</td></tr></tbody></table><table border="1" cellspacing="0" cellpadding="2" width="535"><tbody><tr><td width="71" align="right"><strong>Format</strong></td><td valign="top" width="62" align="center"><strong>Count</strong></td><td valign="bottom" width="62" align="center"><strong>Comp</strong></td><td valign="top" width="85" align="center"><strong>Response</strong></td><td valign="top" width="76" align="center"><strong>Deseria</strong>.</td><td valign="top" width="85" align="center"><strong>Complete</strong></td><td valign="top" width="92" align="right"><strong>Transfered</strong></td></tr><tr><td width="71" align="right">bin</td><td valign="bottom" width="62" align="right">100 <br />100</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">3 <br />2</td><td valign="top" width="76" align="right">29 <br />2</td><td valign="top" width="85" align="right">32 <br />4</td><td valign="top" width="92" align="right">18.611 <br />8.831</td></tr><tr><td width="71" align="right">xml</td><td valign="top" width="62" align="right">100 <br />100</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />2</td><td valign="top" width="76" align="right">1 <br />1</td><td valign="top" width="85" align="right">2 <br />3</td><td valign="top" width="92" align="right">33.649 <br />6.312</td></tr><tr><td width="71" align="right">jsondata</td><td valign="top" width="62" align="right">100 <br />100</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="76" align="right">2 <br />3</td><td valign="top" width="85" align="right">3 <br />4</td><td valign="top" width="92" align="right">23.005 <br />5.713</td></tr><tr><td width="71" align="right">jsonweb</td><td valign="top" width="62" align="right">100 <br />100</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />2</td><td valign="top" width="76" align="right">6 <br />6</td><td valign="top" width="85" align="right">7 <br />8</td><td valign="top" width="92" align="right">15.905 <br />5.397</td></tr><tr><td width="71" align="right">jsonnet</td><td valign="top" width="62" align="right">100 <br />100</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1 <br />1</td><td valign="top" width="76" align="right">2 <br />2</td><td valign="top" width="85" align="right">3 <br />3</td><td valign="top" width="92" align="right">15.905 <br />5.397</td></tr><tr><td width="71" align="right">custom</td><td valign="top" width="62" align="right">100 <br />100</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right"><br />1</td><td valign="top" width="76" align="right">1 <br />1</td><td valign="top" width="85" align="right">1 <br />2</td><td valign="top" width="92" align="right">11.304 <br />5.125</td></tr></tbody></table><table border="1" cellspacing="0" cellpadding="2" width="535"><tbody><tr><td width="71" align="right"><strong>Format</strong></td><td valign="top" width="62" align="center"><strong>Count</strong></td><td valign="bottom" width="62" align="center"><strong>Comp</strong></td><td valign="top" width="85" align="center"><strong>Response</strong></td><td valign="top" width="76" align="center"><strong>Deseria</strong>.</td><td valign="top" width="85" align="center"><strong>Complete</strong></td><td valign="top" width="92" align="right"><strong>Transfered</strong></td></tr><tr><td width="71" align="right">bin</td><td valign="bottom" width="62" align="right">1.000 <br />1.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">11 <br />20</td><td valign="top" width="76" align="right">42 <br />26</td><td valign="top" width="85" align="right">53 <br />46</td><td valign="top" width="92" align="right">177.927 <br />80.243</td></tr><tr><td width="71" align="right">xml</td><td valign="top" width="62" align="right">1.000 <br />1.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">5 <br />15</td><td valign="top" width="76" align="right">8 <br />10</td><td valign="top" width="85" align="right">13 <br />25</td><td valign="top" width="92" align="right">329.784 <br />59.224</td></tr><tr><td width="71" align="right">jsondata</td><td valign="top" width="62" align="right">1.000 <br />1.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">6 <br />13</td><td valign="top" width="76" align="right">23 <br />26</td><td valign="top" width="85" align="right">29 <br />39</td><td valign="top" width="92" align="right">229.068 <br />53.906</td></tr><tr><td width="71" align="right">jsonweb</td><td valign="top" width="62" align="right">1.000 <br />1.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">11 <br />18</td><td valign="top" width="76" align="right">61 <br />65</td><td valign="top" width="85" align="right">72 <br />83</td><td valign="top" width="92" align="right">158.068 <br />50.626</td></tr><tr><td width="71" align="right">jsonnet</td><td valign="top" width="62" align="right">1.000 <br />1.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">5 <br />11</td><td valign="top" width="76" align="right">19 <br />21</td><td valign="top" width="85" align="right">24 <br />32</td><td valign="top" width="92" align="right">158.068 <br />50.626</td></tr><tr><td width="71" align="right">custom</td><td valign="top" width="62" align="right">1.000 <br />1.000</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">2 <br />8</td><td valign="top" width="76" align="right">7 <br />5</td><td valign="top" width="85" align="right">9 <br />13</td><td valign="top" width="92" align="right">112.067 <br />47.624</td></tr></tbody></table><table border="1" cellspacing="0" cellpadding="2" width="535"><tbody><tr><td width="71" align="right"><strong>Format</strong></td><td valign="top" width="62" align="center"><strong>Count</strong></td><td valign="bottom" width="62" align="center"><strong>Comp</strong></td><td valign="top" width="85" align="center"><strong>Response</strong></td><td valign="top" width="76" align="center"><strong>Deseria</strong>.</td><td valign="top" width="85" align="center"><strong>Complete</strong></td><td valign="top" width="92" align="right"><strong>Transfered</strong></td></tr><tr><td width="71" align="right">bin</td><td valign="bottom" width="62" align="right">10.000 <br />10.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">125 <br />210</td><td valign="top" width="76" align="right">528 <br />215</td><td valign="top" width="85" align="right">653 <br />425</td><td valign="top" width="92" align="right">1.770.091 <br />793.997</td></tr><tr><td width="71" align="right">xml</td><td valign="top" width="62" align="right">10.000 <br />10.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">47 <br />148</td><td valign="top" width="76" align="right">114 <br />114</td><td valign="top" width="85" align="right">161 <br />262</td><td valign="top" width="92" align="right">3.284.950 <br />587.571</td></tr><tr><td width="71" align="right">jsondata</td><td valign="top" width="62" align="right">10.000 <br />10.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">53 <br />143</td><td valign="top" width="76" align="right">377 <br />267</td><td valign="top" width="85" align="right">430 <br />410</td><td valign="top" width="92" align="right">2.287.996 <br />535.119</td></tr><tr><td width="71" align="right">jsonweb</td><td valign="top" width="62" align="right">10.000 <br />10.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">113 <br />177</td><td valign="top" width="76" align="right">651 <br />711</td><td valign="top" width="85" align="right">764 <br />888</td><td valign="top" width="92" align="right">1.577.996 <br />502.584</td></tr><tr><td width="71" align="right">jsonnet</td><td valign="top" width="62" align="right">10.000 <br />10.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">47 <br />112</td><td valign="top" width="76" align="right">211 <br />254</td><td valign="top" width="85" align="right">258 <br />366</td><td valign="top" width="92" align="right">1.577.996 <br />502.584</td></tr><tr><td width="71" align="right">custom</td><td valign="top" width="62" align="right">10.000 <br />10.000</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">27 <br />77</td><td valign="top" width="76" align="right">48 <br />72</td><td valign="top" width="85" align="right">75 <br />149</td><td valign="top" width="92" align="right">1.117.995 <br />472.300</td></tr></tbody></table><table border="1" cellspacing="0" cellpadding="2" width="535"><tbody><tr><td width="71" align="right"><strong>Format</strong></td><td valign="top" width="62" align="center"><strong>Count</strong></td><td valign="bottom" width="62" align="center"><strong>Comp</strong></td><td valign="top" width="85" align="center"><strong>Response</strong></td><td valign="top" width="76" align="center"><strong>Deseria</strong>.</td><td valign="top" width="85" align="center"><strong>Complete</strong></td><td valign="top" width="92" align="right"><strong>Transfered</strong></td></tr><tr><td width="71" align="right">bin</td><td valign="bottom" width="62" align="right">100.000 <br />100.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1.389 <br />2.255</td><td valign="top" width="76" align="right">11.064 <br />8.115</td><td valign="top" width="85" align="right">12.453 <br />10.370</td><td valign="top" width="92" align="right">17.701.551 <br />7.887.661</td></tr><tr><td width="71" align="right">xml</td><td valign="top" width="62" align="right">100.000 <br />100.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">457 <br />1.424</td><td valign="top" width="76" align="right">916 <br />1.161</td><td valign="top" width="85" align="right">1.373 <br />2.585</td><td valign="top" width="92" align="right">32.890.892 <br />5.876.977</td></tr><tr><td width="71" align="right">jsondata</td><td valign="top" width="62" align="right">100.000 <br />100.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">523 <br />1.300</td><td valign="top" width="76" align="right">2.594 <br />2.772</td><td valign="top" width="85" align="right">3.117 <br />4.072</td><td valign="top" width="92" align="right">22.887.368 <br />5.349.086</td></tr><tr><td width="71" align="right">jsonweb</td><td valign="top" width="62" align="right">100.000 <br />100.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">1.116 <br />1.781</td><td valign="top" width="76" align="right">6.676 <br />6.692</td><td valign="top" width="85" align="right">7.792 <br />8.743</td><td valign="top" width="92" align="right">15.787.368 <br />5.023.391</td></tr><tr><td width="71" align="right">jsonnet</td><td valign="top" width="62" align="right">100.000 <br />100.000</td><td valign="bottom" width="62" align="center">gzip</td><td valign="top" width="85" align="right">462 <br />1.163</td><td valign="top" width="76" align="right">2.124 <br />2.261</td><td valign="top" width="85" align="right">2.568 <br />3.424</td><td valign="top" width="92" align="right">15.787.368 <br />5.023.391</td></tr><tr><td width="71" align="right">custom</td><td valign="top" width="62" align="right">100.000 <br />100.000</td><td valign="bottom" width="63" align="center">gzip</td><td valign="top" width="85" align="right">221 <br />842</td><td valign="top" width="76" align="right">518 <br />761</td><td valign="top" width="85" align="right">739 <br />1.603</td><td valign="top" width="92" align="right">11.187.367 <br />4.721.865</td></tr></tbody></table><p>Wie obige Zahlen zeigen, schlägt die kleinere Datenmenge sofort zu Buche. Das Fehlen jeglicher Typbehandlung ist in der Responsetime und der sehr kurzen Zeit für die Deserialisierung ebenfalls deutlich zu sehen.</p><p>Die vorgestellte Methodik ist beileibe noch kein Produktivcode. Sie soll lediglich zeigen, dass auch einfache Ansätze zu guten Ergebnissen führen können.</p><p>Der Beispielcode ist als Projekt <a title="Repository bei Bitbucket" href="https://bitbucket.org/klaus_b/serializetesting" rel="cvs nofollow">SerializeTesting</a> bei Bitbucket gehostet. Erweiterungen um eigene Ideen sind ausdrücklich erwünscht.</p><h4>Fazit:</h4><p>Es muss nicht immer ein Serialisierer mit der kompletten Funktionalität verwendet werden. <br />Wie bereits der Vergleich der gängigen Serialisierer untereinander gezeigt hat, kostet Overhead einfach Zeit. Ob nun das erzeugte Format oder die interne Verarbeitung der zu de/serialisierenden Objekte spielt dabei keine Rolle. Wenn konsequent auf jeglichen Overhead verzichtet wird, ist das daraus resultierende Ergebnis immer schlank und effizient. Wie weit die Entschlackung getrieben wird, ist jedem selbst überlassen.</p><div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:db4c629e-4166-45b5-a64d-7ece2600da3f" class="wlWriterEditableSmartContent">Technorati-Tags: <a href="http://technorati.com/tags/binary" rel="tag">binary</a> | <a href="http://technorati.com/tags/xml" rel="tag">xml</a> | <a href="http://technorati.com/tags/json" rel="tag">json</a> | <a href="http://technorati.com/tags/serialization" rel="tag">serialization</a> | <a href="http://technorati.com/tags/c%23" rel="tag">c#</a> | <a href="http://technorati.com/tags/asp.net" rel="tag">asp.net</a></div>

Binär, XML und JSON-Serialisierung im Vergleich von am Fri, 30 Dec 2011 15:21:03 +0100:
<p><img class="leftImg" title="binärer Code" alt="binär code" src="http://blog.klaus-b.net/image.axd?picture=binary.png" />In einem Kommentar zum <a title="Datensammlungen über HTTP serialisieren" href="http://blog.klaus-b.net/post/2011/12/27/Datensammlungen-uber-HTTP-serialisieren.aspx">vorherigen Artikel</a> hatte <a title="Der-Albert.com sein Blog!" href="http://der-albert.com/" rel="met colleague">Albert Weinert</a> angeregt, auch <a title="JavaScript Object Notation" href="http://de.wikipedia.org/wiki/JSON" rel="wiki nofollow">JSON</a> in den Vergleich mit einzubeziehen. <br />Da JSON, <a title="JavaScript" href="http://de.wikipedia.org/wiki/JavaScript" rel="wiki nofollow">JavaScript</a> und Konsorten wirklich nicht meine Welt sind, hat kurzer Hand Albert den Vergleich um drei verschiedene JSON Serialisierer ergänzt. Wer “den Albert” kennt, weis dass er keine halben Sachen macht. Ich schickte im ein kleines Testprojekt und erhielt ein komplettes Benchmark Programm zurück. Im Verlauf der Kommunikation brachte mir Albert auch JSON etwas näher und korrigierte damit meinen Blickwinkel. JSON bedeutet zwar namentlich <strong>JavaScript Object Notation</strong> und in der <a title="Wikipedia" href="http://www.wikipedia.org/" rel="wiki nofollow">Wikipedia</a> steht in der Erläuterung dazu:</p><blockquote><p>Jedes gültige JSON-Dokument soll ein gültiges JavaScript sein …</p></blockquote><p>Dies bedeutet aber nicht unbedingt, das JSON nur für JavaScript verwendet werden kann. Vielmehr stellt das JSON Format ein schlankes Textabbild des serialisierten Objekts ohne großen Overhead dar. Die gängigen JSON Serialisierer bieten alle sehr einfach zu handhabende Methoden zum Serialisieren und Deserialisieren von Objekten.</p><p>Zum direkten Vergleich der Effektivität verschiedener Methoden und Formate der Serialisierung, sind jetzt insgesamt fünf Serialisierer am Start. Die im Benchmark verwendeten Formatkürzel sowie die entsprechenden Produkte sind wie folgt aufgeschlüsselt.</p><ol><li><strong>bin:           </strong><a title="BinaryFormatter Klasse" href="http://msdn.microsoft.com/library/y50tb888.aspx" rel="msdn nofollow">BinaryFormatter</a></li><li><strong>xml:</strong>          <a title="XmlSerializer Klasse" href="http://msdn.microsoft.com/library/swxzdhc0.aspx" rel="msdn nofollow">XmlSerializer</a></li><li><strong>jsondata:</strong>  <a title="DataContractJsonSerializer Klasse" href="http://msdn.microsoft.com/library/bb908432.aspx" rel="msdn nofollow">DataContractJsonSerializer</a></li><li><strong>jsonweb:</strong>  <a title="JavaScriptSerializer Klasse" href="http://msdn.microsoft.com/library/bb337495.aspx" rel="msdn nofollow">JavaScriptSerializer</a></li><li><strong>jsonnet:</strong>    <a title="Json.NET - Serialize all the things" href="http://json.codeplex.com/">Json.NET</a> von <a title="James Newton-King - No pressure, no diamonds" href="http://james.newtonking.com/" rel="colleague">James Newton-King</a>. </li></ol><p>Um einheitliche Testbedingungen zu schaffen, wird für jeden Durchgang die benötigte Anzahl an Datensätzen einmal erzeugt und für alle Serialisierer verwendet. Jeder Serialisierer wird zweimal, einmal mit und einmal ohne <a title="gzip Kompression" href="http://de.wikipedia.org/wiki/Gzip" rel="wiki nofollow">GZip</a> Komprimierung, verwendet und die benötigte Zeit in Millisekunden sowie die übertragene Datenmenge in Bytes erfasst. Es werden fünf Durchgänge mit 10, 100, 1.000, 10.000 und 100.000 Elementen ausgewertet.</p><table border="2" cellspacing="0" cellpadding="2" width="500" align="center"><tbody><tr><td valign="top" width="82" align="center"><strong>Format</strong></td><td valign="top" width="71" align="center"><strong>Count</strong></td><td valign="top" width="116" align="center"><strong>Compression</strong></td><td valign="top" width="112" align="center"><strong>Milliseconds</strong></td><td valign="top" width="115" align="center"><strong>Bytes Transfered</strong></td></tr><tr><td width="82">bin</td><td valign="top" width="71" align="right">10 <br />10</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1 <br />2</td><td valign="top" width="115" align="right">2.609 <br />1.390</td></tr><tr><td width="82">xml</td><td valign="top" width="71" align="right">10 <br />10</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1 <br />2</td><td valign="top" width="115" align="right">3.638 <br />972</td></tr><tr><td width="82">jsondata</td><td valign="top" width="71" align="right">10 <br />10</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">2 <br />3</td><td valign="top" width="115" align="right">2.318 <br />844</td></tr><tr><td width="82">jsonweb </td><td valign="top" width="71" align="right">10 <br />10</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">2</td><td valign="top" width="115" align="right">1.608 <br />801</td></tr><tr><td width="82">jsonnet</td><td valign="top" width="71" align="right">10 <br />10</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right"><br />1</td><td valign="top" width="116" align="right">1.608 <br />801</td></tr></tbody></table><table border="2" cellspacing="0" cellpadding="2" width="500" align="center"><tbody><tr><td valign="top" width="82" align="center"><strong>Format</strong></td><td valign="top" width="71" align="center"><strong>Count</strong></td><td valign="top" width="116" align="center"><strong>Compression</strong></td><td valign="top" width="112" align="center"><strong>Milliseconds</strong></td><td valign="top" width="115" align="center"><strong>Bytes Transfered</strong></td></tr><tr><td width="82">bin</td><td valign="top" width="71" align="right">100 <br />100</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">2 <br />3</td><td valign="top" width="115" align="right">18.559 <br />8.832 </td></tr><tr><td width="82">xml</td><td valign="top" width="71" align="right">100 <br />100</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1 <br />2</td><td valign="top" width="115" align="right">33.344 <br />6.243 </td></tr><tr><td width="82">jsondata</td><td valign="top" width="71" align="right">100 <br />100</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1 <br />3</td><td valign="top" width="115" align="right">22.934 <br />5.717</td></tr><tr><td width="82">jsonweb </td><td valign="top" width="71" align="right">100 <br />100</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">8 <br />5</td><td valign="top" width="115" align="right">15.834 <br />5.382 </td></tr><tr><td width="82">jsonnet</td><td valign="top" width="71" align="right">100 <br />100</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1 <br />2</td><td valign="top" width="116" align="right">15.834 <br />5.382 </td></tr></tbody></table><table border="2" cellspacing="0" cellpadding="2" width="500" align="center"><tbody><tr><td valign="top" width="82" align="center"><strong>Format</strong></td><td valign="top" width="71" align="center"><strong>Count</strong></td><td valign="top" width="116" align="center"><strong>Compression</strong></td><td valign="top" width="112" align="center"><strong>Milliseconds</strong></td><td valign="top" width="115" align="center"><strong>Bytes Transfered</strong></td></tr><tr><td width="82">bin</td><td valign="top" width="71" align="right">1.000 <br />1.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">16 <br />21</td><td valign="top" width="115" align="right">177.939 <br />80.238 </td></tr><tr><td width="82">xml</td><td valign="top" width="71" align="right">1.000 <br />1.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">9 <br />22</td><td valign="top" width="115" align="right">329.675 <br />58.287 </td></tr><tr><td width="82">jsondata</td><td valign="top" width="71" align="right">1.000 <br />1.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">9 <br />14</td><td valign="top" width="115" align="right">228.905 <br />53.838</td></tr><tr><td width="82">jsonweb </td><td valign="top" width="71" align="right">1.000 <br />1.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">17 <br />22</td><td valign="top" width="115" align="right">157.905 <br />50.209 </td></tr><tr><td width="82">jsonnet</td><td valign="top" width="71" align="right">1.000 <br />1.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">7 <br />43</td><td valign="top" width="116" align="right">157.905 <br />50.209 </td></tr></tbody></table><table border="2" cellspacing="0" cellpadding="2" width="500" align="center"><tbody><tr><td valign="top" width="82" align="center"><strong>Format</strong></td><td valign="top" width="71" align="center"><strong>Count</strong></td><td valign="top" width="116" align="center"><strong>Compression</strong></td><td valign="top" width="112" align="center"><strong>Milliseconds</strong></td><td valign="top" width="115" align="center"><strong>Bytes Transfered</strong></td></tr><tr><td width="82">bin</td><td valign="top" width="71" align="right">10.000 <br />10.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">125 <br />214</td><td valign="top" width="115" align="right">1.772.619 <br />795.178 </td></tr><tr><td width="82">xml</td><td valign="top" width="71" align="right">10.000 <br />10.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">69 <br />185</td><td valign="top" width="115" align="right">3.298.716 <br />580.353 </td></tr><tr><td width="82">jsondata</td><td valign="top" width="71" align="right">10.000 <br />10.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">62 <br />130</td><td valign="top" width="115" align="right">2.290.386 <br />536.307</td></tr><tr><td width="82">jsonweb </td><td valign="top" width="71" align="right">10.000 <br />10.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">116 <br />183</td><td valign="top" width="115" align="right">1.580.386 <br />500.012 </td></tr><tr><td width="82">jsonnet</td><td valign="top" width="71" align="right">10.000 <br />10.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">51 <br />116</td><td valign="top" width="116" align="right">1.580.386 <br />500.012 </td></tr></tbody></table><table border="2" cellspacing="0" cellpadding="2" width="500" align="center"><tbody><tr><td valign="top" width="82" align="center"><strong>Format</strong></td><td valign="top" width="71" align="center"><strong>Count</strong></td><td valign="top" width="116" align="center"><strong>Compression</strong></td><td valign="top" width="112" align="center"><strong>Milliseconds</strong></td><td valign="top" width="115" align="center"><strong>Bytes Transfered</strong></td></tr><tr><td width="82">bin</td><td valign="top" width="71" align="right">100.000 <br />100.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1.441 <br />2.248</td><td valign="top" width="115" align="right">17.700.411 <br />7.883.815 </td></tr><tr><td width="82">xml</td><td valign="top" width="71" align="right">100.000 <br />100.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">883 <br />1.584</td><td valign="top" width="115" align="right">32.885.009 <br />5.782.222 </td></tr><tr><td width="82">jsondata</td><td valign="top" width="71" align="right">100.000 <br />100.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">582 <br />1.328</td><td valign="top" width="115" align="right">22.886.615 <br />5.349.126</td></tr><tr><td width="82">jsonweb </td><td valign="top" width="71" align="right">100.000 <br />100.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">1.151 <br />1.833</td><td valign="top" width="115" align="right">15.786.615 <br />4.987.282 </td></tr><tr><td width="82">jsonnet</td><td valign="top" width="71" align="right">100.000 <br />100.000</td><td valign="bottom" width="116" align="right">gzip</td><td valign="top" width="112" align="right">570 <br />1.156</td><td valign="top" width="116" align="right">15.786.615 <br />4.987.282 </td></tr></tbody></table><p>Wie in obigen Tabellen ersichtlich, setzen sich die beiden JSON Serialisierer <em>JavaScriptSerializer</em> und <em>Json.NET</em> in der Datengröße deutlich ab. Interessant auch die Beobachtung, dass beide Serialisierer die exakt gleiche Datenmenge übertragen. <br /><em>Json.NET</em> scheint, im Vergleich zum <em>JavaScriptSerializer,</em> effizientere Methoden zur Serialisierung zu verwenden, da er die Daten zum Teil doppelt so schnell liefert.</p><p>Interessierte können sich das Projekt gerne für eigene Tests herunterladen.</p><p><a title="SerializeTesting.zip herunterladen" href="http://klaus-b.net/Downloads/Download.aspx?fileID=05b569a0-6bba-4717-9bb3-69931b58a6f0" rel="nofollow">SerializeTesting.zip</a></p><h3>Fazit:</h3><p>Wer, wie ich, bis dato JSON bei der Serialisierung immer übersehen hat, sollte dies schleunigst ändern. Man sollte einfach das JS für JavaScript in JSON nicht überbewerten. Beherzigt man diesen Rat, hat man ein schlankes und schnelles Format zur Serialisierung der meisten .NET Objekte. <br />An dieser Stelle möchte ich mich noch einmal besonders bei Alber Weinert für die tatkräftige Unterstützung und das zurechtrücken meiner Sichtweise bedanken.</p><div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:8eba222a-6268-4b9c-b1f4-ca98a47872ba" class="wlWriterEditableSmartContent">Technorati-Tags: <a href="http://technorati.com/tags/binary" rel="tag">binary</a> | <a href="http://technorati.com/tags/xml" rel="tag">xml</a> | <a href="http://technorati.com/tags/json" rel="tag">json</a> | <a href="http://technorati.com/tags/serialization" rel="tag">serialization</a> | <a href="http://technorati.com/tags/c%23" rel="tag">c#</a> | <a href="http://technorati.com/tags/asp.net" rel="tag">asp.net</a></div>

Datensammlungen über HTTP serialisieren von am Tue, 27 Dec 2011 12:15:10 +0100:
<p><img class="leftImg" title="binär code" alt="binärer Code" src="http://blog.klaus-b.net/image.axd?picture=binary.png" width="294" height="172" />...aber in welchem Format? <br />Mein erster Kandidat - und heimlicher Favorit - war hier die binäre Serialisierung mit dem <a title="BinaryFormatter Klasse" href="http://msdn.microsoft.com/library/y50tb888.aspx" rel="msdn nofollow">BinaryFormatter</a>. <br />Für einen Vergleich habe ich den <a title="XmlSerializer Klasse" href="http://msdn.microsoft.com/library/swxzdhc0.aspx" rel="msdn nofollow">XmlSerializer</a> herangezogen, wobei ich Anfangs die binäre Serialisierung als klaren Favoriten ansah. Bei lokaler Verwendung, zum speichern von Kollektionen wie etwa <a title="List(T) Klasse" href="http://msdn.microsoft.com/library/6sh2ey19.aspx" rel="msdn nofollow">List<T></a> oder <a title="Collection(T) Klasse" href="http://msdn.microsoft.com/library/ms132397.aspx" rel="msdn nofollow">Collection<T></a> in einer Datei, zeigte der BinaryFormatter immer sehr gute Ergebnisse. Die hohe Datendichte bei vergleichsweise kleiner Dateigröße ist einer seiner großen Vorteile gegenüber der Serialisierung in eine Datei im <a title="Extensible Markup Language" href="http://de.wikipedia.org/wiki/XML" rel="wiki nofollow">XML</a>-Format. Warum sollte dieser Vorteil nicht auch beim Transport über <a title="Hypertext Transfer Protocol" href="http://de.wikipedia.org/wiki/Http" rel="wiki nofollow">HTTP</a> zum tragen kommen?</p><p><img class="rightImg" title="TestNode" alt="TestNode" src="http://blog.klaus-b.net/image.axd?picture=TestNode.png" width="259" height="223" />Nun aber der Reihe nach. <br />In einem aktuellem Projekt ist eine der Anforderungen, eine Auflistung von Informationen über einzelne Knoten eines Netzwerks bereitzustellen. Diese Auflistung ändert sich mit der Verfügbarkeit einzelner Knoten und soll an einem zentralen Ort, für alle Knoten zugänglich, bereitgestellt werden. <br />Die Funktionalität entspricht in etwa einem <a title="rendezvous host" href="http://en.wikipedia.org/wiki/Bootstrapping_node" rel="wiki nofollow">Boostrapping Node</a>, oder Rendezvous Host, eines <a title="Peer-to-Peer P2P" href="http://de.wikipedia.org/wiki/Peer-to-Peer" rel="wiki nofollow">Peer-to-Peer</a> Netzes. Wie an der Aufgabenstellung bereits ersichtlich, geht es hier um die Kommunikation zwischen Maschinen. HTTP wurde als Transportprotokoll gewählt, um Probleme mit der <a title="Erklärung des Begriffs Firewall" href="http://de.wikipedia.org/wiki/Firewall" rel="wiki nofollow">Firewall</a> der beteiligten Maschinen zu vermeiden. <br />Die Visualisierung der Daten in irgendwelchen UIs spielt keine Rolle und ist auch nicht vorgesehen. Auf die Befindlichkeiten von Benutzern braucht nicht eingegangen zu werden. Insofern kann auf die Notwendigkeit der Kompatibilität zu Formaten wie <a title="JavaScript Object Notation" href="http://de.wikipedia.org/wiki/JSON" rel="wiki nofollow">JSON</a> udgl. verzichtet werden. <br />Für die Testumgebung habe ich eine vereinfachte Klasse, siehe obige Grafik, verwendet, die sich auf wenige öffentliche Eigenschaften und die überschriebene Methode <a title="Object.ToString Methode" href="http://msdn.microsoft.com/library/7bxwbwt2.aspx" rel="msdn nofollow">ToString</a> beschränkt. Für eine Simulation und Begutachtung der Bedingungen ist dies vorerst vollkommen ausreichend. <br />Zum bereitstellen der Kollektion wird ein <a title="IHttpHandler Interface" href="http://msdn.microsoft.com/library/7ezc17x8.aspx" rel="msdn nofollow">IHttpHandler</a> in einer <a title="ASP.NET" href="http://msdn.microsoft.com/library/bb400852.aspx" rel="msdn nofollow">ASP.NET</a>-Anwendung verwendet, der die Auflistung in der benötigten Größe dynamisch erzeugt. Die Namen der Knoten werden mit dem Präfix <em>Node_</em> durchnummeriert. Die MessagingUrl wird aus dem Namen erzeugt und mit einem fiktiven Handler ergänzt. Der Token wird mit der Zeichenfolgendarstellung einer <a title="Guid Struktur" href="http://msdn.microsoft.com/cey1zx63.aspx" rel="msdn nofollow">Guid</a> simuliert und die Liste der Eigenschaft Ranges wird in zufälliger Länge mit zufälligem Inhalt erzeugt. <br />Der Handler verarbeitet die beiden Parameter <em>format</em> und <em>size</em>. Mit <em>format</em> wird entweder binäre oder XML serialisiert. <em>Size</em> gibt die Anzahl der Elemente in der Auflistung an.</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:c9309851-0a86-4529-965b-e8b2ec7ca6e6" class="wlWriterEditableSmartContent" 0px?="0px?" padding-top:="padding-top:" none;="none;" float:="float:" inline;="inline;" display:="display:"><pre name="code" class="c#:firstline[23]">public void ProcessRequest(HttpContext context) { if (context == null) { return; } var format = context.Request.QueryString["format"]; var size = int.Parse( context.Request.QueryString["size"], CultureInfo.InvariantCulture); this.data = this.data ?? SerializationHandler.CreateData(size); context.Response.ContentEncoding = Encoding.UTF8; switch (format) { case "bin": context.Response.ContentType = "application/octet-stream"; var binFormatter = new BinaryFormatter(); binFormatter.Serialize(context.Response.OutputStream, this.data); break; case "xml": default: context.Response.ContentType = "application/xml"; var xmlFormatter = new XmlSerializer(typeof(List<TestNode>)); xmlFormatter.Serialize(context.Response.OutputStream, this.data); break; } } private static List<TestNode> CreateData(int entriesCount) { var list = new List<TestNode>(entriesCount); var i = 0; var lengthRandom = new Random(); var contenRandom = new Random(); while (i < entriesCount) { var node = new TestNode(); node.Name = "Node_" + i.ToString("D6", CultureInfo.InvariantCulture); node.MessagingUrl = "http://node." + node.Name + ".net/messaging.axd"; node.Token = Guid.NewGuid().ToString(); var length = lengthRandom.Next(1, 10); node.Ranges = new List<int>(length); var j = 0; while (j < length) { node.Ranges.Add(contenRandom.Next(1, 254)); j++; } list.Add(node); i++; } return list; }</pre></div><p>Wie bereits weiter oben angesprochen, ist lediglich die Größe der übertragenen Daten von vorrangigem Interesse, da die Auflistung häufigen Änderungen unterworfen ist und oft abgerufen werden kann. Zur Bestimmung der Größe der übertragenen Daten bei der unterschiedlichen Anzahl der Einträge, verwende ich den Composer von <a title="Fiddler - Http Debugging Proxy" href="http://www.fiddler2.com/fiddler2/" rel="nofollow">Fiddler</a>, der sehr einfach die Änderung des <a title="Erklärung Query String" href="http://de.wikipedia.org/wiki/Query_String" rel="wiki nofollow">Query-String</a> und der <a title="Erklärung der Request Header Felder" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.3" rel="nofollow">Request-Header</a> zulässt.</p><p>Für einen ersten Vergleich habe ich eine Auflistung jeweils binär und als XML serialisiert mit 100, 1.000, 10.000 und 100.000 Einträgen verglichen. (Siehe folgende Grafik)</p><p><img class="centerImg" title="erster einfacher Test" alt="einfacher Test" src="http://blog.klaus-b.net/image.axd?picture=plainTest.png" width="528" height="266" /></p><p>Wie erwartet, ist die übertragene Datenmenge der XML-serialisierten Auflistungen beinahe doppelt so groß wie die binär serialisierten. Auch interessant zu beobachten war die Tatsache, dass die XML-Serialisierung schneller als die binär-Serialisierung wurde, je weiter die Anzahl der Elemente stieg. Bei 1.000 Einträgen benötigte binär 92ms und XML 69ms für den Transport. <br />Bei 10.000 Einträgen 298ms zu 92ms und bei 100.000 Einträgen immerhin schon 1800ms zu 820ms.</p><p>Als nächste Maßnahme zog ich die Kompression der übertragenen Daten via <a title="gzip Kompression" href="http://de.wikipedia.org/wiki/Gzip" rel="wiki nofollow">GZip</a> in Betracht. Die Kompression wird nicht vom <a title="Microsoft Internet Information Services" href="http://de.wikipedia.org/wiki/Microsoft_Internet_Information_Services" rel="wiki nofollow">IIS</a> , sondern von einem eigenem <a title="IHttpModule Interface" href="http://msdn.microsoft.com/library/wc2zw2d4.aspx" rel="msdn nofollow">IHttpModule</a> vorgenommen. Damit bleibt mehr Spielraum für weitere Optimierungen. <br />Mit der Kompression sollte XML einen Vorsprung erfahren, da sich erfahrungsgemäß Textinhalte besser komprimieren lassen als binäre Daten.</p><p><img class="centerImg" title="Test mit Komprimierung" alt="Test der Komprimierung" src="http://blog.klaus-b.net/image.axd?picture=compressedTest.png" width="535" height="265" /></p><p>Wie obige Grafik zeigt, lassen sich auch die binären Daten noch auf über die Hälfte ihrer ursprünglichen Größe komprimieren. XML zieht, in der reinen Größe der übertragenen Daten, mit einem Kompressionsfaktor von rund 5,8 deutlich vorbei. <br />Da die Kompression Zeit kostet, sind die Transportzeiten beinahe gleich. Erst bei 100.000 Einträgen macht XML mit rund 1,8s gegenüber 2,5s etwas Boden gut.</p><p>Als vorläufig letzte Maßnahme, kommt noch ein <a title="HttpResponse.Filter Eigenschaft" href="http://msdn.microsoft.com/library/xtzd8yzd.aspx" rel="msdn nofollow">HttpResponse.Filter</a> zum Einsatz, der überflüssige Zeilenumbrüche und Leerzeichen aus dem generierten XML entfernt. Das Prinzip eines solchen Filters, habe ich schon einmal in einem <a title="Nutzlosen Whitespace zur Laufzeit aus dem HTML entfernen" href="http://blog.klaus-b.net/post/2010/02/13/Nutzlosen-Whitespace-zur-Laufzeit-aus-dem-HTML-entfernen.aspx">früheren Artikel</a> beschrieben. </p><p><img class="centerImg" title="Test mit Komprimierung und Whitespace Filter" alt="Test mit Komprimierung und Filter" src="http://blog.klaus-b.net/image.axd?picture=compressedFilterTest.png" width="538" height="268" /></p><p>Der Filter brachte noch einmal eine Ersparnis von rund 3.5% der übertragenen Datenmenge. <br />Allerdings auf Kosten der Zeit, denn das Filtern ist relativ zeitaufwendig. Bei 100.000 Datensätzen verlängerte sich die Transportzeit von 1,8s auf stolze 3,1s und somit langsamer als die binäre Serialisierung der gleichen Datensätze.</p><p>Eine weitere Möglichkeit die Datenmenge zu reduzieren, besteht in der Minimierung des ausgegeben XML. Dabei werden die Namen der Tags durch entsprechend kurze Zeichenfolgen ersetzt. Z.B.: <MessagingUrl> durch <MU> oder <Token> durch <T>. Mit den Daten aus dem obigen Beispiel, können so noch einmal rund 8% eingespart werden. <br />Es darf dann nicht vergessen werden, beim Deserialisieren des XML, die Minimierung wieder rückgängig zu machen.</p><h3>Fazit:</h3><p>Die Einstellung zu meinem anfänglichem Favoriten hat sich geändert. Wenn eine überschaubare Menge an Daten einfach und effizient serialisiert werden sollen, ist der BinaryFormatter nach wie vor die erste Wahl. <br />Soll die Serialisierung maximal optimiert werden, bietet der XmlSerializer deutlich mehr Möglichkeiten ohne einen komplett eigenen Serializer entwickeln zu müssen.</p><div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:b39fe043-017e-4d73-b9a6-8bb9f551d526" class="wlWriterEditableSmartContent">Technorati-Tags: <a href="http://technorati.com/tags/serialization" rel="tag">serialization</a> | <a href="http://technorati.com/tags/xml" rel="tag">xml</a> | <a href="http://technorati.com/tags/binary" rel="tag">binary</a> | <a href="http://technorati.com/tags/asp.net" rel="tag">asp.net</a> | <a href="http://technorati.com/tags/c%23" rel="tag">c#</a></div>

404 Fehlerbehandlung in BlogEngine.NET 2.0 von am Sun, 19 Jun 2011 15:59:36 +0100:
<p><img class="leftImg" title="BlogEngine.NET" alt="BlogEngine.NET" src="http://blog.klaus-b.net/image.axd?picture=logo.png" /></p><p>Wie ich bereits in den beiden früheren Artikeln <a title="BlogEngine.NET und 404 Fehler" href="http://blog.klaus-b.net/post/2010/02/19/BlogEngineNET-und-404-Fehler.aspx">BlogEngine.NET und 404 Fehler</a> sowie <a title="Eine bessere 404 Fehlerbehandlung in Blogengine.NET" href="http://blog.klaus-b.net/post/2010/03/06/Eine-bessere-404-Fehlerbehandlung-in-BlogengineNET.aspx">Eine bessere 404 Fehlerbehandlung in Blogengine.NET</a> beschrieben habe, gibt die 404-Fehlerbehandlung von <a title="Blogengine.NET. An innovative open source blogging platform developed with ASP.NET 2.0" href="http://www.dotnetblogengine.net/" rel="nofollow">BlogEngine.NET</a> immer wieder Anlass zur Kritik. Auch in der aktuellen Version 2.0 hat sich daran kaum etwas geändert. Zwar wird per Standard in der <em>web.config</em> richtigerweise die Methode <em>ResponseRewrite</em> zur Weiterleitung auf die Fehlerseite angegeben, aber damit erschöpft es sich auch schon in einer sauberen Behandlung von 404-Fehlern.</p><p>Als gröbsten Fehler sehe ich hier den zurückgelieferten Statuscode 200 OK an, wenn die Fehlerseite <em>error.aspx</em> angezeigt wird. Für einen Benutzer mag das keine Rolle spielen, aber die Bots von Suchmaschinen sehen das ganz anders. Da wird die angezeigte Fehlerseite statt der falsch angeforderten Seite in den Index eingetragen. Für die Suchmaschinen ist demzufolge die eigentlich nicht vorhandene Seite sehr wohl vorhanden. <br />Dem kann leicht abgeholfen werden, in dem mit der Fehlerseite error404.aspx auch der Statuscode 404 ausgeliefert wird. Dazu einfach, am Ende der Methode <a title="Control.Load Ereignis" href="http://msdn.microsoft.com/abk3yt37.aspx" rel="msdn nofollow">Page_Load</a>, den Statuscode 404 an die Eigenschaft <em>StatusCode</em> des <em>Response</em>-Objekts übergeben.</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:242d0512-243b-46b7-ae29-c10fc9746ca5" class="wlWriterEditableSmartContent"><pre name="code" class="c#">Page.Title += Server.HtmlEncode(" - " + "Seite nicht gefunden"); Response.StatusCode = (int)HttpStatusCode.NotFound;</pre></div><p>Diese kleine Maßnahme zeigt sofort den gewünschten Effekt. Ohne 302-Weiterleitung wird sofort der Status 404 zurückgegeben.</p><p><img class="centerImg" title="Statuscode 404 mit ResponseWrite" alt="Statuscode 404 mit ResponseWrite" src="http://blog.klaus-b.net/image.axd?picture=rewriteStatus404.png" width="520" height="162" /></p><p>Eine weitere, sehr unschöne, Tatsache stellt der Versuch der Verwendung des Wertes <em>aspxerrorpath</em> aus dem <a title="HttpRequest.QueryString Eigenschaft" href="http://msdn.microsoft.com/ktw1eyx1.aspx" rel="msdn nofollow">QueryString</a> dar. Dieser Wert wird bei der Verwendung der <em>ResponseWrite</em> Methode von <a title="ASP.NET" href="http://msdn.microsoft.com/bb400852.aspx" rel="msdn nofollow">ASP.NET</a> nicht mehr erzeugt. Somit wurde ein sehr schönes Feature der Seite <em>error404.aspx</em>, die Suche nach ähnlichen Seiten wie der angegebenen, komplett ausgehebelt. <br />Nun, mit einem kleinen Eingriff in die Fehlerseite <em>error404.aspx</em> lässt sich dieser Umstand wieder beheben wie folgender Screenshot zeigt. Dabei wird bei der, aus obigem Screenshot, falsch angeforderten Seite <em>BlogEngine.NET Update auf Version 2.0.0.69</em> die richtige Seite <em>BlogEngine.NET Update auf Version 2.0.0.66</em> vorgeschlagen.</p><p><img class="centerImg" title="Fehlerseite mit Titelvorschlag" alt="error404.aspx" src="http://blog.klaus-b.net/image.axd?picture=error404aspx.png" width="520" height="333" /></p><p>Da, wie bereits oben angesprochen, der Eintrag <em>aspxerrorpath</em> im QueryString nicht mehr zur Verfügung steht, muss der Pfad der falsch angeforderten Seite auf andere Weise ermittelt werden. Am besten eignet sich hierfür die Eigenschaft <em>Message</em> der zuletzt geworfenen Ausnahme. Da ASP.NET auf die Fehlerseite eines 404-Fehlers umgeleitet hat, sollte auch ein solcher von der Methode <a title="HttpServerUtility.GetLastError Methode" href="http://msdn.microsoft.com/066kfzc0.aspx" rel="msdn nofollow">GetLastError</a> des <em>Server</em>-Objekts der aktuellen Seite zurückgegeben werden. Jetzt muss nur noch der Pfad der nicht gefundenen Seite aus der Fehlernachricht extrahiert werden und der Ablauf kann erfolgen wie bisher. Um den Pfad der falsch angeforderten Seite für die gesamte Klasse zur Verfügung zu stellen, speichere ich diesen in einer klassenweiten Variablen. <br />Um die Änderungen ersichtlicher zu gestalten, habe ich den ursprünglichen Code nur auskommentiert. Als erstes wird die Methode <em>Page_Load</em> angepasst:</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:aa8066fb-abd8-4a1c-9616-3d0b810a753b" class="wlWriterEditableSmartContent"><pre name="code" class="c#:firstline[12]">/// <summary> /// Hält den Pfad der falsch angeforderten Seite /// </summary> /// <remarks>n/a</remarks> private string errorPath; protected void Page_Load(object sender, EventArgs e) { this.errorPath = this.Request.QueryString["aspxerrorpath"]; var ex = this.Server.GetLastError(); if (string.IsNullOrEmpty(this.errorPath) && ex != null) { var index = ex.Message.IndexOf("/post/"); var temp = ex.Message.Substring(index); this.errorPath = temp.Substring(0, temp.IndexOf(" ")); } //if (Request.QueryString["aspxerrorpath"] != null // && Request.QueryString["aspxerrorpath"].Contains("/post/")) if (!string.IsNullOrEmpty(this.errorPath) && this.errorPath.Contains("/post/")) { DirectHitSearch(); divDirectHit.Visible = true; } else if (Request.UrlReferrer == null) { divDirectHit.Visible = true; } else if (Request.UrlReferrer.Host == Request.Url.Host) { divInternalReferrer.Visible = true; } else if (GetSearchKey() != string.Empty) { SearchTerm = GetSearchTerm(GetSearchKey()); BindSearchResult(); divSearchEngine.Visible = true; } else if (Request.UrlReferrer != null) { divExternalReferrer.Visible = true; } Page.Title += Server.HtmlEncode(" - " + "Seite nicht gefunden"); Response.StatusCode = (int)HttpStatusCode.NotFound; }</pre></div><p>Damit mögliche passende Seiten vorgeschlagen werden können, muss auch die Methode <em>DirectHitSearch</em> eine kleine Änderung erfahren:</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:bcc4c13f-f896-40b7-8e3e-6fee5693b027" class="wlWriterEditableSmartContent"><pre name="code" class="c#:firstline[59]">private void DirectHitSearch() { string from = this.errorPath; //Request.QueryString["aspxerrorpath"]; int index = from.LastIndexOf("/") + 1; string title = from.Substring(index) .Replace(".aspx", string.Empty).Replace("-", " "); List<IPublishable> items = Search.Hits(title, false); if (items.Count > 0) { LiteralControl result = new LiteralControl( string.Format( "<li><a href=\"{0}\">{1}</a></li>", items[0].RelativeLink.ToString(), items[0].Title)); phSearchResult.Controls.Add(result); } }</pre></div><h4></h4><h4>Fazit:</h4><p>Dies war bestimmt nicht die letzte Anpassung von Fehlerseiten unter BlogEngine.NET. <br />Für Heute oder Morgen war ursprünglich der Release Kandidat für BlogEngine.NET 2.5 angekündigt. Da im Moment das Repository auf <a title="CodePlex - Open Source Project Hosting" href="http://www.codeplex.com/" target="_blank">CodePlex</a> beschädigt ist, wird sich die Veröffentlichung ein wenig verzögern. <br />BlogEngine.NET hat sich seit der Veröffentlichung der Version 2.0 sehr zum Positiven verändert. Es wurde und wird viel für eine breitere Akzeptanz getan. Doch solange sich solche “Feature” im Code verbergen, wird sich an der breiten Meinung: “nur für .NET Bastler” nicht viel ändern.</p><div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:6a634984-6b6b-4c3c-b915-006c44248f37" class="wlWriterEditableSmartContent">Technorati-Tags: <a href="http://technorati.com/tags/blogengine.net" title="blogengine.net" rel="tag">blogengine.net</a> | <a href="http://technorati.com/tags/asp.net" title="asp.net" rel="tag">asp.net</a> | <a href="http://technorati.com/tags/404+error" title="404 error" rel="tag">404 error</a> | <a href="http://technorati.com/tags/seo" title="seo" rel="tag">seo</a></div>

Wie kann ich meine BlogEngine.NET Erweiterungen lokalisieren? von am Wed, 15 Jun 2011 17:09:11 +0100:
<p><img class="leftImg" title="BlogEngine.NET" alt="BlogEngine.NET" src="http://blog.klaus-b.net/image.axd?picture=logo.png" /></p><p>Die unter <a title="Blogengine.NET. An innovative open source blogging platform developed with ASP.NET 2.0" href="http://www.dotnetblogengine.net/" rel="nofollow">BlogEngine.NET</a> verwendeten <a title="BlogEngine.NET Extensions" href="http://blogengine.codeplex.com/wikipage?title=Extensions&referringTitle=Documentation">Extensions</a>, zum Erweitern des Funktionsumfangs, bieten keine der üblichen Möglichkeiten um die Textausgabe dieser Erweiterungen zu lokalisieren. Diese Erweiterungen sind im Normalfall Klassen, die mit dem Extension-Attribut versehen und im <em>App_Code</em>-Ordner einer BlogEngine Installation im Unterordner <em>Extensions</em> abgelegt werden. Eine klassenbezogene Lokalisierung ist im .NET Framework nicht vorgesehen. <br />BlogEngine bietet lediglich die Methode <em>Translate</em> der <em>Utils</em>-Klasse um Zeichenfolgen mit Hilfe der globalen Ressourcen zu übersetzen. Um diese Option zu nutzen, müssen alle zu verwendenden Zeichenfolgen in den globalen Ressourcen der Webanwendung vorhanden sein. <br />Also auch kein gangbarer Weg, da der Aufwand bei Aktualisierungen der globalen Ressourcen einfach zu groß währe.</p><p>Ich entschied mich für die Verwendung von statischen Klassen als <a title="Geschachtelte Typen (C#-Programmierhandbuch)" href="http://msdn.microsoft.com/library/ms173120.aspx" rel="msdn nofollow">geschachtelte Typen</a> innerhalb meiner Erweiterungen. Eine anwendungsweite Lösung erschient mir nicht praktikabel, da gerade Erweiterungen oft einzeln verteilt und verwendet werden. Wenn jetzt noch jedes mal eine zusätzliche Assembly mit verteilt werden soll, kann die einfache Handhabung der Erweiterungen schnell unübersichtlich und unnötig kompliziert werden.</p><p>In meinem Ansatz mache ich mir die Tatsache zu nutze, dass in den Einstellungen des Blog jederzeit die verwendete Spracheinstellung abrufbar ist. Von der ausgegebene Einstellung, z.B.: de-DE, verwende ich lediglich das Sprachschema. Auf die regionalen Unterschiede zwischen de-DE und de-AT habe ich bewusst verzichtet, da ich nur Zeichenfolgen darstellen will. <br />So kann in einem <em>switch/case</em>-Block, entsprechend der eingestellten Sprache, die jeweils zugehörige Zeichenfolge ermittelt werden. <br />Da die geschachtelte Klasse als statisch deklariert ist, können die enthaltenen Eigenschaften, wie von der Resource-Klasse gewohnt, einfach direkt verwendet werden.</p><div id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:685666bd-131f-403c-9dff-bfe2027e8094" class="wlWriterEditableSmartContent"><pre name="code" class="c#">/// <summary> /// Konvertiert die Style-Attribute von Html-Images in /// eine entsprechende CSS-Klasse. /// </summary> /// <remarks>n/a</remarks> [Extension( "Converts the style attributes of Html-Images to the corresponding CSS class.", "1.5.0.0", "<a href=\"http://blog.klaus-b.net/\">klaus_b</a>")] public class ConvertStyleToCss { // Hier andere Member der Klasse #region Methods private static void InitialiceSettings() { var settings = new ExtensionSettings("ConvertStyleToCss") { IsScalar = true }; settings.AddParameter( "leftImg", LocalResources.LeftImg, 50, true, false, ParameterType.String); settings.AddValue("leftImg", "leftImg"); settings.AddParameter( "rightImg", LocalResources.RightImg, 50, true, false, ParameterType.String); settings.AddValue("rightImg", "rightImg"); settings.AddParameter( "centerImg", LocalResources.CenterImg, 50, true, false, ParameterType.String); settings.AddValue("centerImg", "centerImg"); settings.AddParameter( "cleanWlwDivs", LocalResources.CleanWlwDivs, 4, false, false, ParameterType.Boolean); settings.AddValue("cleanWlwDivs", false); // TODO: Hilfe erzeugen und einfügen ConvertStyleToCss.setting = ExtensionManager.InitSettings( "ConvertStyleToCss", settings); } #endregion Methods #region Nested Types /// <summary> /// Stellt lokalisierte Zeichenfolgen als Ressourcen bereit. /// </summary> /// <remarks>Wird nur von der übergeordneten Klasse verwendet.</remarks> private static class LocalResources { #region Fields /// <summary> /// Hält die eingestellte Kultur des Blogs. /// </summary> /// <remarks>n/a</remarks> private static readonly string blogCulture = BlogSettings.Instance.Culture; /// <summary> /// Hält die zu verwendende Sprache des Blogs. /// </summary> /// <remarks>n/a</remarks> private static readonly string culture = blogCulture.Substring(0, 2); /// <summary> /// Hält den zu verwendenden Hilfetext. /// </summary> /// <remarks>n/a</remarks> private static string helpText; #endregion Fields #region Properties internal static string CenterImg { get { switch (culture) { case "de": return "Die CSS-Klasse für zentrierte Bilder:"; default: return "CSS class for centered images:"; } } } internal static string CleanWlwDivs { get { switch (culture) { case "de": return "WLW DIVs bereinigen:"; default: return "Clean WLW DIVS:"; } } } internal static string HelpText { get { if (string.IsNullOrEmpty(helpText)) { switch (culture) { case "de": helpText = CreateGermanHelpText(); break; default: helpText = CreateEnglishHelpText(); break; } } return helpText; } } internal static string LeftImg { get { switch (culture) { case "de": return "Die CSS-Klasse für links ausgerichtete Bilder:"; default: return "CSS class for left aligned images:"; } } } internal static string RightImg { get { switch (culture) { case "de": return "Die CSS-Klasse für rechts ausgerichtete Bilder:"; default: return "CSS class for right aligned images:"; } } } #endregion Properties #region Methods private static string CreateEnglishHelpText() { var sb = new StringBuilder(); // TODO: create english help text return sb.ToString(); } private static string CreateGermanHelpText() { var sb = new StringBuilder(); // TODO: deutschen Hilfetext erzeugen return sb.ToString(); } #endregion Methods } #endregion Nested Types }</pre></div><p>Im Moment habe ich die <em>switch/case</em>-Blöcke so gestaltet, dass bei allen anderen Sprachen außer Deutsch, ein englischer Text ausgegeben wird. Da ich die Konfigurationsoberfläche meiner Erweiterungen gerne in der Sprache meines Blog darstelle, denke ich eine gangbare Lösung für Anwender meiner Erweiterungen gefunden zu haben, die andere Sprachen bevorzugen.</p><h4>Fazit:</h4><p>Die Lokalisierung von Oberflächen, Meldungen und anderen Textausgaben sollte eigentlich schon lange den Kinderschuhen entwachsen sein. Leider muss ich immer wieder feststellen, dass dem nicht so ist. Entweder liegt dieser Umstand an den jeweiligen Entwicklern die es nicht für nötig erachten ihre Anwendungen zu lokalisieren, oder aber die entsprechenden Technologien erscheinen zu kompliziert um in jeder Anwendung einfach verwendet werden zu können.</p><div id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:380e97eb-bfc1-4350-b69d-f7ad3827771f" class="wlWriterEditableSmartContent">Technorati-Tags: <a href="http://technorati.com/tags/blogengine.net" title="blogengine.net" rel="tag">blogengine.net</a> | <a href="http://technorati.com/tags/asp.net" title="asp.net" rel="tag">asp.net</a> | <a href="http://technorati.com/tags/c%23" title="c#" rel="tag">c#</a> | <a href="http://technorati.com/tags/localization" title="localization" rel="tag">localization</a></div>

Bewerte diesen Blog:

Einloggen
Nutzername:
Passwort:
Passwort vergessen
Noch kein Mitglied?
Blog des Jahres
Tauschbörse
Der Swapy-Blog ist ein Weblog von der ersten online Tauschbörse mit Auktionscharkter. Die Tauschbörse ist unter www.swapy.de zu erreichen. Das Motto der Tauschbörse ist "Tauschen statt kaufen". Swapy.de ermöglicht alles zu tauschen was man gar nicht mehr braucht in etwas was man evtl. sucht. Der Swapy-Blog bietet hierbei viele Informationen und News rund um die Tauschbörse. Vorbei schauen lohnt sich.
Verzeichnisse

Weitere empfehlenswerte Blog Verzeichnisse, RSS Verzeichnisse, Artikel Verzeichnisse, News Portale und Social Bookmarking-Dienste:

Artikel Verzeichnis Blog-Feed Blog Marketing Blog Verzeichnis Social Bookmark und RSS-Dienst Blogsy.de - das Blognetzwerk
Social Bookmark Sript

Webseiten Tipp

Kennen Sie schon?



| Site Map | Impressum | Nutzungsbedingungen | FAQ | Vorteile | Kontakt |

Auto Blogs | Community Blogs | Computer Blogs | Finanz Blogs | Firmen Blogs | Foto Blogs | Freizeit Blogs | Gesundheit Blogs | Haus und Garten Blogs | Humor Blogs | Internet Blogs | Kommunikation | Kultur Blogs | Kunst Blogs | Literatur Blogs | Musik Blogs | News Blogs | Politik Blogs | Reisen Blogs | SEO Blogs | Sonstige Blogs | Spiele Blogs | Sport Blogs | Städte Blogs | Tagebuch Blogs | Tiere Blogs | TV - Film
© 2008 Blog-Webkatalog.de - Blog Verzeichnis, Blogverzeichnis, Blogwebkatalog, Weblog-Katalog

Free Thumbshots von Thumbshots.de