Dnes pro zajímavost menší demo kód pro LINQ to XML, o němž jsem cca včera zde napsal toto:

"ve skutečnosti se jedná o zcela novou knihovnu pro práci s XML (alternativu DOMu v podobě XmlDocument anebo streaming technologií XmlReader/XmlWriter). Jedná se o třídy pro reprezentaci XML takovou, aby se pohodlně vytvářela a přitom bylo možné použít LINQ to IEnumerable<T>. Kromě toho obsahuje řadu extenzních funkcí specifických pro hierarchie, např. AncestorsAndSelf působícími nad IEnumerable<XElement>. Veškeré funkce najdete v knihovně System.Xml.Linq. Více někdy příště."

Příště je právě teď. LINQ to XML je LINQ-friendly anebo přesněji řečeno IEnumerable<T>-friendly knihovna pro manipulaci s XML dokumenty. Je částečnou alternativou XmlReader/XmlWriter a úplnou alternativou DOM knihovny. DOM je přece jenom už přes 10 let starý a vytvářený s ohledem na prohlížeče a JavaScript. V této podobě byl převzat do .NET frameworku i se všemi nevýhodami. A přece jenom - možnosti moderních jazyků již jsou dnes někde jinde.

Mimořádně nepraktické je programové vytváření DOM dokumentu (dokument, vytvořit přes něj element, nastavit co je třeba, přidat do dokumentu). LINQ to XML nabízí vytváření dokumentů i jejich fragmentů bez nutnosti "jít přes dokument". Navíc docela vtipně používá konstruktor XElement(XName, params object[]), kde za params lze do konstruktoru zadat libovolný počet prvků odvozených z XNode pro vytvoření podřazených nodů - velmi komfortní.

DOM má též dost nešťastně řešenou práci se jmennými prostory (na omluvu DOMu lze uvést, že v době jeho vzniku v podstatě nebyly používány). I zde se LINQ to XML poučil a zavádí třídy XName a XNameSpace. Autoři rozhraní byli opět docela elegantní, implicitní konverze string -> XName anebo přetížený operátor XNamespace+string -> XName mne příjemně překvapily, jakoby u jejich vymýšlení i někdo přemýšlel Smile

Jinak s obsahem XDocument lze provádět standardní LINQ manipulace a kromě toho má i speciální metody pro procházení hierarchií - viz vzorový kód níže. O LINQ to XML (pořád mám tendence psát XLINQ) se můžete více dočíst třeba zde.

Ještě dovětek pro visualbasicáře - ve VB.NET je možná ještě jednodušší syntaxe typu Dim karel= <contact><name>Karel</name></contact>, na mne už je to ale trochu moc Wink

Program.cs:

using System.Diagnostics;

using System.Linq;

using System.Xml;

using System.Xml.Linq;

using System.Xml.XPath;

 

namespace LINQtoXML

{

    class Program

    {

        static void Main(string[] args)

        {

            // Pohodlna programova konstrukce XML dokumentu je asi nejvetsi vyhoda

            XDocument kontakty1 = new XDocument(

                new XProcessingInstruction("aaa-bbb-ccc","ddd-eee"),

                new XElement("Lide",

                    new XElement("Osoba",new XElement("Jmeno","Karel"),new XElement("Prijmeni","Novak")),

                    new XElement("Osoba",new XElement("Jmeno","Jana"),new XElement("Prijmeni","Novakova"))

                    )

                );

            // A nyni v porovnani s XmlDocument

            XmlDocument kontakty2 = new XmlDocument();

            kontakty2.AppendChild(kontakty2.CreateProcessingInstruction("aaa-bbb-ccc","ddd-eee"));

            XmlElement lide = kontakty2.CreateElement("Lide");

            kontakty2.AppendChild(lide);

            XmlElement osoba1 = kontakty2.CreateElement("Osoba");

            lide.AppendChild(osoba1);

            XmlElement jmeno1 = kontakty2.CreateElement("Jmeno");

            jmeno1.InnerText = "Karel";

            osoba1.AppendChild(jmeno1);

            XmlElement prijmeni1 = kontakty2.CreateElement("Prijmeni");

            prijmeni1.InnerText = "Novak";

            osoba1.AppendChild(prijmeni1);

            XmlElement osoba2 = kontakty2.CreateElement("Osoba");

            lide.AppendChild(osoba2);

            XmlElement jmeno2 = kontakty2.CreateElement("Jmeno");

            jmeno2.InnerText = "Jana";

            osoba2.AppendChild(jmeno2);

            XmlElement prijmeni2 = kontakty2.CreateElement("Prijmeni");

            prijmeni2.InnerText = "Novakova";

            osoba2.AppendChild(prijmeni2);

 

            // Programova konstrukce na zaklade vysledku jineho LINQ dotazu (treba ze seznamu LINQ to Objects)

            var kontakty3 = new XElement("Lide",

                from o in Clovek.VzorekLidi select

                    new XElement("Osoba", new XElement("Jmeno", o.CeleJmeno), new XElement("Vek",o.Vek))

                );

 

            // Velmi elegantne jsou resena jmena elementu pomoci XName a XNamespace

            // Maji totiz implicitni konverze a zaroven predefinovany operator +

            // Jmenny prostor lze definovat dvojim zpusobem

            XNamespace prostor1 = "http://schemas.prostor1.cz";

            XElement prostory = new XElement("Obal",

                new XElement(prostor1 + "PrvniElement", "Jedna"),

                new XElement("{http://schemas.prostor2.cz}DruhyElement", "Dva")

                );

            // Lze tez ovlivnit vystup pomoci definice prefixu

            XElement prostoryPrefix = new XElement("Obal",

                new XAttribute(XNamespace.Xmlns + "p1", prostor1),

                new XElement(prostor1 + "PrvniElement", "Jedna"),

                new XElement("NekvalifikovanyElement", "Nevim")

                );

            // Pri vetsim poctu opakovani je vyhodnejsi definovat si rovnou XName

            XName prvniElement = prostor1 + "PrvniElement";

            XElement prostoryOpakovani = new XElement("Obal",

                new XAttribute(XNamespace.Xmlns + "p1", prostor1),

                new XElement(prvniElement, "Jedna"),

                new XElement(prvniElement, "Ein")

                );

           

            // Lze delat dotazy pomoci LINQ metod anebo pomoci specifickych extenznich metod pro XML

            XElement contacts = XElement.Load("Contacts.xml");

            // Pouziti specialnich metod pro XML definovanych v LINQ to XML

            var vyberVsichniPotomci = contacts.Element("contact").DescendantNodes();

            var vyberVsechnyPodelementy = contacts.Element("contact").Descendants();

            var vyberPrimiPotomci = contacts.Element("contact").Nodes();

            var vyberPrimePodelementy = contacts.Element("contact").Elements();

            var pocetTelefonu = contacts.XPathEvaluate("count(contact/phone)");

            var vyberVsechnyTelefony = contacts.XPathSelectElements("contact/phone");           

            // Zakladni LINQ konstrukce select/from/where/orderby

            var vyberZaklad1 = contacts.Elements("contact").Where(c => (int)c.Element("netWorth") > 10).OrderBy(c => (string) c.Element("name"));

            var vyberZaklad2 = from c in contacts.Elements("contact") where (int)c.Element("netWorth") > 10 orderby (string)c.Element("name") select c;

            // Samozrejme lze delat i slozitejsi dotazy, zplostovani, prevod z XML do normalnich objektu apod.

            var vyberZplosteni = contacts.Elements("contact").SelectMany(c => c.Elements("phone").Select(p => new { Name = (string) c.Element("name"), Phone = (string) p } ));

 

            // Samozrejme tez lze s objekty LINQ to XML manipulovat (pridavat, mazat, menit)

            XElement prvniosoba = contacts.Element("contact");

            prvniosoba.Add(new XElement("phone", new XAttribute("type", "fax"), "3232-343-23"));

            XElement homePhone = prvniosoba.Elements("phone").First(p => p.Attribute("type").Value == "home");

            homePhone.ReplaceNodes("999-88-777");

            homePhone.AddAfterSelf(new XElement("phone", new XAttribute("type", "mobile"), "555-66-555"));

            prvniosoba.Elements("phone").First(p => p.Attribute("type").Value == "work").Remove();

 

            Debugger.Break();

        }

    }

}

Clovek.cs:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LINQtoXML

{

    class Clovek

    {

        public string CeleJmeno { get; set; }

        public int Vek { get; set; }

        public bool MaVousy { get; set; }

 

        public string[] Jmena

        {

            get { return CeleJmeno.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); }

        }

 

        public static List<Clovek> VzorekLidi = new List<Clovek>() {

                new Clovek{CeleJmeno="Pavel Novak", Vek=35, MaVousy=false},

                new Clovek{CeleJmeno="Ho Ci Min", Vek=72, MaVousy=false},

                new Clovek{CeleJmeno="Javier Perez Juarez Simon Miguel Diaz", Vek=47, MaVousy=true},

                new Clovek{CeleJmeno="Kato", Vek=24, MaVousy=false},

                new Clovek{CeleJmeno="Michael Jurek", Vek=19, MaVousy=false},

                new Clovek{CeleJmeno="Vladimir Iljic Lenin", Vek=52, MaVousy=true},

                new Clovek{CeleJmeno="Karel Novak", Vek=35, MaVousy=true},

                new Clovek{CeleJmeno="Karel Jaromir Erben", Vek=62, MaVousy=false}

        };

    }

}

Contacts.xml:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<!--LINQ to XML Contacts XML Example-->

<?MyApp 123-44-4444?>

<contacts>

  <contact>

    <name>Patrick Hines</name>

    <phone type="home">206-555-0144</phone>

    <phone type="work">425-555-0145</phone>

    <address>

      <street1>123 Main St</street1>

      <city>Mercer Island</city>

      <state>WA</state>

      <postal>68042</postal>

    </address>

    <netWorth>10</netWorth>

  </contact>

  <contact>

    <name>Gretchen Rivas</name>

    <phone type="mobile">206-555-0163</phone>

    <address>

      <street1>123 Main St</street1>

      <city>Mercer Island</city>

      <state>WA</state>

      <postal>68042</postal>

    </address>

    <netWorth>11</netWorth>

  </contact>

  <contact>

    <name>Scott MacDonald</name>

    <phone type="home">925-555-0134</phone>

    <phone type="mobile">425-555-0177</phone>

    <address>

      <street1>345 Stewart St</street1>

      <city>Chatsworth</city>

      <state>CA</state>

      <postal>91746</postal>

    </address>

    <netWorth>500000</netWorth>

  </contact>

</contacts>