Tipp: StringReader und StringWriter

Ein kleiner Tipp für die Leute, die .NET Version 3.5 oder höher einsetzen:

Wer Text an Methoden / Klassen weitergeben will, die mit Streams arbeiten, sollte dies nicht umständlich über den MemoryStream lösen, sondern sich lieber mal die beiden Klassen StringReader und StringWriter anschauen. Geht deutlich einfacher und man braucht nicht umständlich mit irgendwelchen Encodings / Bytes / Stream-Positionen rumspielen.

XSLT Transformation in C#

Ich hatte ein recht umfangreiches Xml-Schema. Dieses wird ständig mal erweitert und eine andere Applikation benötigt nur einen Teil dieses Schemas. Dazu wurde es immer von Hand für die andere Applikation geändert. Um mir das Leben einfacher zu machen, wollte ich dies über eine XSLT-Transformation lösen, schließlich ist ein Schema auch nur eine XML-Datei. Ein großer Teil wurde einfach nur kopiert, dennoch sah die Ausgabe anders aus als die Eingabe. Mein Kopiertemplate sah folgendermaßen aus:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Einfach alle Nodes und Attribute kopieren, also nichts wildes. Dennoch wurde aus:

<xs:element name="comment" type="xs:string" minOccurs="0" />

folgendes:

<xs:element name="comment" type="xs:string" minOccurs="0"></xs:element>

Das Schema ist noch valide und funktioniert tadellos, nur sieht es nicht wirklich schön aus. Nachdem ich im Netz nach einen bestimmten Parameter im C#-Code und ähnliches gesucht hatte, wurde ich doch noch fündig. Das .NET-Framework bietet zwei verschiedene Methoden zur Transformation.
XslTransform und XslCompiledTransform. Der Kommentar in der MSDN sagt eigentlich alles:

Die XslCompiledTransform-Klasse ist ein XSLT-Prozessor, der die XSLT 1.0 Syntax unterstützt. Dies ist eine neue Implementierung, die im Vergleich zur veralteten XslTransform-Klasse die Leistung verbessert. Die Struktur der XslCompiledTransform-Klasse ist der XslTransform-Klasse sehr ähnlich.

Nebenbei werden auch noch das ShortEndTag unterstützt, womit mein Problem gelöst wäre. Der Vollständigkeit halber sollte noch auf diesen Blogeintrag hingewiesen werden: XslCompiledTransform Slower than XslTransform?, denn auch noch die veraltete XslTransform-Klasse scheint noch ihre Daseinsberechtigung zu haben.

WiiMC – Steuerung

Falls noch wer WiiMC nutzt, der sollte sich mal die Dokumentationsseite ansehen. Dort ist schön aufgelistet, welche Knöpfe auf der WiiMote welche Bedeutung haben, grade im Videomodus. Habe mir den wichtigen Teil ausgedruckt und liegt nun unterm Tisch, so kann ich im Film zum Beispiel zwischen den Sprachen hin- und herschalten.

Wii Homebrew: WiiMC

Ich nutze auf meiner Wii eine Homebrew namens WiiMC, das Programm macht aus einer Wii eine doch nette Multimedia-Station. So kann ich meine DVDs schauen, Filme übers Netzwerk oder USB-Stick. Aber auch Musik oder Bilder kann ich mir anschauen. Einzig mit HD-Material kommt die Wii nicht klar, dazu ist der CPU einfach zu langsam.

Also die App WiiMC ist eigentlich nur ein grafischer Aufsatz für den MPlayer. MPlayer ist zwar sehr mächtig, doch absolut nicht bedienbar. Einstellungen müssen umständlich am PC gemacht werden usw. Bei WiiMC kann man alle Einstellungen direkt an der Wii über eine virtuelle Tastatur machen. Nun habe ich dort eine SMB-Freigabe eingerichtet. Diese hat allerdings den Nachteil, dass man dort nur eine IP und keinen DNS-Namen angeben kann. Leider kam es schon ein paar mal vor, dass die IP geändert werden musste. Also erstmal am PC schauen, wie lautet die IP und dann nachpflegen. Das ging mir auf den Keks, mithilfe eines Computers geht auch ein qualifizierter DNS-Name.

Dazu greift per PC auf die SD-Karte zu und schaut mal in den Ordner /apps/WiiMC/. Dort befindet sich eine settings.xml. In der Sektion „Network“ könnt ihr nun die IP finden:

&lt;section name=&quot;Network&quot; description=&quot;Network Settings&quot;&gt;
	&lt;smbshare name=&quot;0&quot;&gt;
		&lt;variable name=&quot;ip&quot; value=&quot;192.168.0.10&quot; description=&quot;SMB Share IP&quot; /&gt;
		&lt;variable name=&quot;share&quot; value=&quot;MyShareName&quot; description=&quot;SMB Share Name&quot; /&gt;
		&lt;variable name=&quot;user&quot; value=&quot;username&quot; description=&quot;SMB Share Username&quot; /&gt;
		&lt;variable name=&quot;pwd&quot; value=&quot;password&quot; description=&quot;SMB Share Password&quot; /&gt;
		&lt;variable name=&quot;displayname&quot; value=&quot;displayName&quot; description=&quot;SMB Display Name&quot; /&gt;
	&lt;/smbshare&gt;
&lt;/section&gt;

Nun einfach unter „ip“ den DNS-Namen eintragen, speichern, fertig. Klappt ohne Probleme, jedoch konnt ihr den Namen nicht auf der Wii anpassen. Ich vermute mal, der DNS-Name funktioniert auch bei FTP-Verbindungen, getestet habe ich es aber nicht.

Löschen von Nodes in einem XML-Dokument

Ich hatte letztens ein XML-Dokument, welches ich etwas verschlanken wollte. Das Format war in etwa folgendes:

<root>
  <node1>Content</node1>
  <node2>Content</node2>
  <node3>Content</node3>
  <node4>Content</node4>
</root>

Ich wollte nur die „node1“ – Node behalten, sprich alle anderen entfernen. Daher mein erster Ansatz:

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("<root><node1>Content</node1><node2>Content</node2><node3>Content</node3><node4>Content</node4></root>");
XPathNavigator navigator = xmlDocument.CreateNavigator();
			
string[] nodesToRemove = new[] {"//node2", "//node3", "//node4"};

foreach (var nodeName in nodesToRemove)
{
	XPathNavigator node = navigator.SelectSingleNode(nodeName);
	if (node != null)
	{
		node.DeleteSelf();
	}
}

Console.WriteLine(xmlDocument.OuterXml);

Funktioniert und gut.

Nun wollte ich den Ansatz umdrehen, und alle Nodes entfernen, die nicht den Namen „node1“ haben.

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("<root><node1>Content</node1><node2>Content</node2><node3>Content</node3><node4>Content</node4></root>");
XPathNavigator navigator = xmlDocument.CreateNavigator();

string xpath = "/root/node()[name()!='node1']";

XPathNodeIterator nodes = navigator.Select(xpath);
while(nodes.MoveNext())
{
	if(nodes.Current != null)
	{
		nodes.Current.DeleteSelf();
	}
}

Console.WriteLine(xmlDocument.OuterXml);

Leider war das Ergebnis nicht so wie erwartet, obwohl der XPath korrekt ist und die anderen Nodes selektiert. Es wurde nur „node2“ gelöscht, sprich die erste Node, welche mit dem NodeIterator angesprochen wird. Durch das DeleteSelf() liefert MoveNext() automatisch false zurück, wenn es wieder aufgerufen wird. Ähnliche Probleme gibt es etwa auch, wenn in einer foreach()-Schleife der Enumerator geändert wird. Eine mögliche Lösung wäre folgendes:

while(nodes.MoveNext())
{
	if(nodes.Current != null)
	{
		nodes.Current.DeleteSelf();
		nodes = navigator.Select(xpath);
	}
}

Nach dem Löschen wird der XPath einfach noch mal ausgeführt. Meiner Meinung nach keine wirklich schöne Lösung, leider aber die einzige die ich gefunden habe, welche auch funktioniert.