Thomas Kramer

IT-COW | All posts tagged 'Lambda'

LINQ und StreamReader, das yield-Schlüsselwort und XML-Validierung

By Administrator at September 09, 2010 21:59
Filed Under: 3.5, C#

Ergänzend zu meinem vorherigen LINQ-Eintrag wollte ich mir einmal das lesende Streamen von XML-Dateien anschauen, besonders weil Herr Parys dazu nur auf einen MSDN-Eintrag verwiesen hat. Ich habe mir dazu das Beispiel aus der MSDN einmal angeschaut:

 

public static class StreamReaderSequence
{
    public static IEnumerable<string> Lines(this StreamReader source)
    {
        String line;

        if (source == null)
            throw new ArgumentNullException("source");
        while ((line = source.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        StreamReader sr = new StreamReader("People.txt");
        XStreamingElement xmlTree = new XStreamingElement("Root",
            from line in sr.Lines()
            let items = line.Split(',')
            where !line.StartsWith("#")
            select new XElement("Person",
                       new XAttribute("ID", items[0]),
                       new XElement("First", items[1]),
                       new XElement("Last", items[2]),
                       new XElement("Occupation", items[3])
                   )
        );
        Console.WriteLine(xmlTree);
        sr.Close();

        Console.ReadKey();
    }
}

 

Wie man sieht wird zuerst eine Erweiterungsmethode für die StreamReader-Klasse implementiert (Lines), die eine IEnumerable-Liste vom Typ string zurückgibt. In einer Schleife

 

while ((line = source.ReadLine()) != null) { [..] }

 

werden dann die Zeilen der Textdatei – die über den Konstruktor von StreamReader angegeben wurde – ausgelesen (in die Variable line) und über das yield-Schlüsselwort in die <string>-Liste des Rückgabetyps übergeben. Zum Schluss wird dann diese IEnumerable<string>-Liste  als ganzes zurückgegeben – es wird also durch das yield-Schlüsselwort automatisch Code vom Compiler generiert.

 

In der Main-Methode sieht man dann die LINQ-Abfrage - zuerst wird ein XStreamingElement instanziiert und als zweiter Parameter dafür dann die LINQ-Abfrage übergeben, die weitere XML-Unterelemente erstellt... und in sr.Lines() ruft dann die Erweiterungsmethode auf.

 

Als zweites habe ich dann noch die XML-Validierung über Quellcode getestet, weil die in seinem Vortrag ebenfalls etwas kurz gekommen war. Ich habe hier ein zweigeteiltes XML-Schema, also ein Schema das auf ein anderes verweist – und das XML-Dokument wird gegen das erste Schema validiert.

 

Leider kann ich diese Schemas nicht auflisten, aber die Validierung funktioniert so über den Quellcode - natürlich wird zuerst das Schema geladen, das keine weiteren Abhängigkeiten aufweist (XML-Schema1):

 

static void Main(string[] args)
{
    XDocument element = XDocument.Load(@"XML-Datei");     
    XmlSchemaSet schemaset = new XmlSchemaSet();


    XmlSchema schema = new XmlSchema();
    System.IO.TextReader readFile = new StreamReader(@"XML-Schema1");
    schemaset.Add("{targetNamespace}", XmlReader.Create(readFile));


    XmlSchema schema2 = new XmlSchema();
    System.IO.TextReader readFile2 = new StreamReader(@"XML-Schema2");
    schemaset.Add("{targetNamespace}", XmlReader.Create(readFile2));

 

    bool errors = false;           
    element.Validate(schemaset, (o, e) => {
                                            Console.WriteLine("{0}", e.Message);
                                            errors = true;
                                          }); 

    Console.WriteLine();
    Console.WriteLine("doc1 {0}", errors ? "did not validate" : "validated");
    Console.ReadKey();
}

 

{targetNamespace} muss natürlich durch den Namespace der jeweiligen XML-Schemas ausgetauscht werden. StreamReader liest hier die Datei ein, wird einem TextReader zugewiesen und XmlReader schlussendlich als Argument übergeben.

 

Über den Lambda-Ausdruck mit 2 Variablen (o, e) werden hier die Validierungsfehlermeldungen ausgegeben – auch mehrere, es ist also keine separate Schleife notwendig. (o, e) bezieht sich hier auf den zweiten Parameter der Methode Validate - den validationEventHandler – und o steht demnach für das Objekt und e für die EventArgs des Handlers.

 

Mit der Klammer {} im Lambda-Ausdruck kann ein beliebiger Code-Block eingefügt werden… Lambda-Ausdrücke sind somit auch für die Abarbeitung von Events interessant. Schon schön, wie stark man das heute abkürzen kann – aber die Validate-Methode gibt auch keinen direkten Wert zurück, eine andere Auswertung ist damit gar nicht möglich.

 

Wenn ein angegebener Typ im Schema fehlt wird direkt eine Exception ausgegeben, aber z. b. bei einem Wert außerhalb der Enumerationsliste im Schema wird korrekt eine Fehlermeldung in der Console aufgelistet.

 

Dieser Code

 

errors ? "did not validate" : "validated"

 

stellt übrigens einen sogenannten ternären Operator dar – wenn die Variable errors true ist nimm den Wert "did not validate", ansonsten "validated" (und schreibe ihn anstelle von 0 in den Ausdruck "doc1 {0}”).

 

Ergänzung 15.09.2010: im Beispiel ganz oben kann auch durchgängig XStreamingElement statt XElement genommen werden, beim Test machte das keinen Unterschied. In der MSDN ist es so wie oben aufgelistet.

 

Weitere Links

 

  • XML-Validierung in der MSDN: Link
  • Erzeugung einer XML-Schemadefinition (MSDN): Link
  • Validierung mithilfe eines eingekapselten XmlReader-Objekts (MSDN): Link

 

Monats-Liste