Tillämpad fysik och elektronik Applikationsutveckling för Internet 5p Lärare: Stefan Berglund & Per Kvarnbrink XML och C#.NET Att hantera XML-dokument Utförd av: Datum: 2006-10-08 E-post: Första inlämningen Rättad av: Datum: Godkänd Retur
Innehållsförteckning 1 Inledning 2 2 XML 2 2.1 Användningsområden 2 2.2.NET och XML 3 2.2.1 Ladda in ett XML-dokument 3 2.2.2 Att välja ut ett specifikt nodelement 4 2.2.3 Hämta data 5 2.2.4 Uppdatera/Ändra 6 2.2.5 Lägga till 7 2.2.6 Ta bort element 8 2.2.7 Funktionen GetNextNodeID() 9 2.2.8 Hämta data med XmlTextReader() 10 3 Gränssnitt 11 4 Tips 12 5 Referenser 12 5.1 Webbplatser 12 5.1.1 XML 12 5.1.2 XML och.net 12 6 Bilagor 13 XML-dokument (databas) 13
1 Inledning Att använda XML som databas i ett webbpubliceringssystem (CMS) kanske inte är det första man tänker på när det finns riktiga lösningar som exempelvis MySQL och SQL-Server. Men varför inte? Kanske har du som utvecklare begränsade resurser som exempelvis budget och behöver en snabb, enkel men ändå funktionell lösning. Tyngden i denna rapport är inte lagt vid att skapa just ett CMS för en webbplats, utan för att beskriva tekniken bakom hantering av XML-dokument i.net. Eftersom XML har blivit en sådan kraftfull standard kan detta vara en god kunskap inför framtida projekt. Exempelvis ger Adobe Flash och XML goda verktyg till kraftfulla dynamiska lösningar. 2 XML XML - extensible Markup Language - är ett enkelt och mycket flexibelt textbaserat filformat som bygger på strukturdefinitionsspråket SGML (Standard Generalized Markup Language) - ISO 8879. XML tillkom i mitten av 1990-talet på initiativ av organisationen W3C och var från början designat för att möta den stora elektroniskt växande marknaden. Eftersom det används för att organisera och strukturera textbaserad information kan den tolkas och bearbetas oberoende av datorplattform. Ett annat exempel på detta är det digitala dokumentformatet PDF från Adobe. Det har tillkommit flera standarder som baseras på XML. Här är några: XHTML - Senaste versionen av HTML MusicXML Öppet filformat för musiknoter SVG (Scalable Vector Graphic) Öppet filformat för att beskriva 2D grafik XSL (extensible Stylesheet Language) Stilmallsformat som bygger på XMLstruktur SMIL (Synchronized Multimedia Integration Language) XML-baserat Språk för att beskriva hur ljud, bilder och text ska kombineras. Jabber Öppet XML-baserat protokoll för meddelandetjänster av samma typ som exempelvis ICQ och MSN. XUL (XML User Interface Language) XML-baserat programmeringsspråk för användargränssnitt. Förmedlat av Mozilla. 2.1 Användningsområden Antalet användningsområden för XML är många och här följer några av dem: Nyhetsflöden på webben (RSS, Atom, Klip etc.) Dokumentformat: MS Office Open XML File Format, Open Document från OASIS. Multimedia Exempelvis Flash och XML Strukturerad lagring av data 2
2.2.NET och XML.NET erbjuder ett flertal klasser för att läsa och skriva XML-dokument, vilka även följer W3C:s XML 1.0 standard. Dessa fem metoder kan man som utvecklare i C# använda sig av: XmlTextReader XmlValidatingReader XmlDocument (och resten av DOM API) XPathNavigator ADO.NET DataSets I denna rapport används klassen XmlDocument som är en cachad träd-representation av XML-filen. XmlDocument är enkelt att använda och implementera men resurskrävande. Generellt sett bör man istället använda klassen XmlReader för bättre prestanda, speciellt vid stora XML-dokument. I slutet av rapporten kommer även ett exempel på hur man använder XmlTextReader-klassen. De NameSpace som används för XML-hanteringen är: using System.IO; // Hantering av filer etc. (In/Out) using System.Xml; // XML-hantering using System.Text; // Kodning av filer (ISO-8859-1 = Latin 1) 2.2.1 Ladda in ett XML-dokument För att ladda in ett dokument med hjälp av XmlDocument-klassen gör man enligt följande. // Initiera en ny instans av XmlDocument-klassen XmlDocument xdoc = new XmlDocument(); // Laddar in XML-dokument (databasen) xdoc.load("databas.xml"); Den XML-fil alla exempel i denna rapport baseras på hittas under bilagor. 3
2.2.2 Att välja ut ett specifikt nodelement Här beskrivs två exempel på hur man kan välja ut ett eller flera element i XML-fil. getelementsbytagname() Man kan välja ut en specifik nod genom att hämta dess tagg-namn. XmlNodeList menuitem = xdoc.getelementsbytagname("menuitem"); Detta hämtar en lista av alla noder med namnet menuitem, dvs. alla <menuitem>taggar i XML-dokumentet. Innehållet kan vi sedan hämta via variabeln menuitem. XPath XPath fungerar ungefär som en sökfunktion där man specificerar vad man vill söka efter. Den bygger på en sorts hierarkistisk struktur precis som nodelementen i ett XMLdokument och XmlNodeList menuitem = xdoc.selectnodes("/pages/page/menuitem"); Förklaring: Första / placerar oss i XML-dokumentets rot. Utifrån roten tar vi oss vidare in i Page, page- och tillsist menuitem-noden. Se strukturexempel nedan: ROT/ <Pages> <page> <menuitem>meny 1</menuitem> XPath erbjuder en mycket mer kraftfull sökning i dokumentet än föregående metod gör eftersom vi bl.a. kan använda wildcards (*), söka på attribut (@) och filtrera sökningen ([]). I nästa XPath-exempel väljer vi ut noden menuitem beroende på vilket id, dvs. attribut page-noden har. XmlNodeList menuitem = xdoc.selectnodes("/pages/page[@id=1]/menuitem"); Vill du läsa mer om XPath kan du göra det här: http://www.developer.com/xml/article.php/3383961 4
2.2.3 Hämta data För att hämta innehållet i alla barnnoder som heter <menuitem> kan man göra enligt följande. // Initiera en ny instans av XmlDocument-klassen XmlDocument xdoc = new XmlDocument(); // Laddar in XML-dokument (databasen) xdoc.load("databas.xml"); // Hämtar noder (xpath) XmlNodeList pagenode = xdoc.selectnodes("/pages/page"); XmlNodeList menuitem = xdoc.selectnodes("/pages/page/menuitem"); XmlNodeList pagecontent = xdoc.selectnodes("/pages/page[@id='2']/content"); // Skriver ut meny och sidinnehåll for (int i = 0; i < menuitem.count; i++) ulmenu.innerhtml += "\n\t" + "<li><a href=\"?id=" + pagenode[i].attributes["id"].value + "\" title=\"" + menuitem[i].innertext + "\">" + menuitem[i].innertext + "</a></li>"; divcontent.innerhtml = "\n" + pagecontent[0].innertext; Sammanfattning av kod: Öppnar XML-dokument Hämta noder Skriver ut specifika nodvärden: alla id-attribut i page-noden, all data i menuitem-noderna samt data i specifik content-nod (beror på vilket id som angetts, i detta fall id=3). Samma sak kan givetvis göras med getelementsbytagname() istället för XPath. 5
2.2.4 Uppdatera/Ändra Med hjälp av XmlWriter-klassen kan man uppdatera specifika noder i ett XMLdokument. // Initiera en ny instans av XmlDocument-klassen XmlDocument xdoc = new XmlDocument(); // Laddar in XML-dokument (databasen) xdoc.load("databas.xml"); // Hämtar data från specifika noder med xpath (id=2) XmlNodeList menuitem = xdoc.selectnodes("/pages/page[@id='2']/menuitem"); XmlNodeList content = xdoc.selectnodes("/pages/page[@id='2']/content"); XmlNodeList datetime = xdoc.selectnodes("/pages/page[@id='2']/datetime"); // Sätter (uppdaterar/ändrar) noder med specifikt namn menuitem[0].innertext = txtmenuitemname.text; content[0].innertext = txtcontent.text; datetime[0].innertext = String.Format("0:yyyy-MM-dd, HH:MM:ss", DateTime.Now.ToLocalTime()); // Skapar ny instans av XmlTextWriter-klassen samt sätter kodning för // dokumentet: ISO-8859-1 = Latin 1. XmlTextWriter writer = new XmlTextWriter(xmlDatabase, Encoding.GetEncoding("ISO-8859-1")); // Bibehåller orginalformatering writer.formatting = Formatting.Indented; xdoc.preservewhitespace = true; // Skriver uppdaterad/ändrad data till XML-filen xdoc.writeto(writer); // Stänger skrivaren writer.close(); Sammanfattning av kod: XML-dokument öppnas Noder som ska uppdateras väljs Nya nod-värden sätts Ny XML-skrivare skapas som sedan skriver de nya värdena till XML-filen. 6
2.2.5 Lägga till Precis som med föregående exempel används här XmlWriter-klassen för att lägga till ett helt nytt page-element med innehållande barnnoder. // Initiera en ny instans av XmlDocument-klassen XmlDocument xdoc = new XmlDocument(); // Laddar in XML-dokument (databasen) xdoc.load("databas.xml"); // Skapar nytt page-element (<page></page>) XmlElement newelem = xdoc.createelement("page"); // Lägg till ett id-attribut (<page id=""></page>) XmlAttribute newattr = xdoc.createattribute("id"); // Sätter unikt sid-id (attribut) till page-elementet. // Funktionen GetNextID() returnerar unikt id. newattr.value = GetNextNodeID().ToString(); // Sätter attributet som sista noden // - Not: Om inte så skrivs inte id till <page>-elementet newelem.attributes.append(newattr); // Skapa barnnoder i <page>-elementet newelem.innerxml = "<menuitem></menuitem><content></content><datetime></datetime>"; newelem.appendchild(xdoc.createwhitespace("\r\n")); // Linefeed newelem["menuitem"].innertext = txtmenuitemname.text; newelem["content"].innertext = txtcontent.text; newelem["datetime"].innertext = String.Format("0:yyyy-MM-dd, HH:MM:ss", DateTime.Now.ToLocalTime()); // Lägger till nytt element med barnnoder och dess innehåll xdoc.documentelement.appendchild(newelem); // Skapar ny instans av XmlTextWriter-klassen samt sätter kodning för // dokumentet: ISO-8859-1 = Latin 1. XmlTextWriter writer = new XmlTextWriter("Databas.xml", Encoding.GetEncoding("ISO-8859-1")); // Bibehåller orginalformatering writer.formatting = Formatting.Indented; xdoc.preservewhitespace = true; // Skriver uppdaterad/ändrad data till XML-filen xdoc.writeto(writer); // Stänger skrivaren writer.close(); Sammanfattning av kod: Öppnar XML-dokument Skapar nytt page-element (<page></page>) Skapar nytt unikt id-attribut för page-elementet (Se kap. 2.2.7) Skapar barnnoder för page-elementet (<menuitem></menuitem><content></content><datetime></datetime>) Skriver ett nytt page-element med tillhörande attribut samt barnnoder till XML-filen. 7
2.2.6 Ta bort element Med funktionen RemoveChild() kan vi enkelt plocka bort ett helt element och allt dess innehåll (noder). // Initiera en ny instans av XmlDocument-klassen XmlDocument xdoc = new XmlDocument(); // Laddar in XML-dokument (databasen) xdoc.load("databas.xml"); // Hämta noden med specifikt sid-id XmlNode node = xdoc.selectsinglenode("/pages/page[@id='0']"); // Hämtar föregående nod från nuvarande, dvs. backar ett snäpp XmlNode commonparent = node.parentnode; // Om noden finns så ta bort den if (node!= null) commonparent.removechild(node); // Spara filen xdoc.save(xmldatabase); Sammanfattning av kod: Öppnar XML-dokument Väljer, med ett XPath-uttryck där id=0, ut specifik nod som ska tas bort Backar uppåt ett snäpp för att hamna utanför page-elementet som ska tas bort Ta bort page-elementet Spara resultatet. 8
2.2.7 Funktionen GetNextNodeID() För att undvika att en ny sida inte får samma id som en redan befintlig, användes denna funktion för att skapa en unik nyckel. private int GetNextNodeID() // Läs XML-filen (strömmande) och lås den så att ingen kan använda den. // Detta hindrar att samma id-returneras två gånger. FileStream xmlfile = new FileStream(Server.MapPath("../App_Data/Database.xml"), FileMode.Open, FileAccess.ReadWrite, FileShare.None); // Lägger filen i XmlTextReader XmlTextReader reader = new XmlTextReader(xmlFile); // Det unika id-numret startar på 0 int nmaxid = 0; // Medan XML-filen läses igenom... while (reader.read()) // Kollar om elementet är ett page-element (<page></page>) if (reader.isstartelement() && reader.name == "page") // Konverterar id till ett heltal int nid = Convert.ToInt32(reader.GetAttribute("id")); // if-satsen ger det högsta id-numret if (nmaxid < nid) nmaxid = nid; // Stäng XML-fil. Filen kan nu användas av alla igen. xmlfile.close(); // Ökar id med +1 och returnerar värdet nmaxid++; return nmaxid; Sammanfattning av kod: Läser XML-filen Hämtar alla ID-värden och sätter en variabel lika med det högsta värdet Lägger på +1 på det högsta värdet, vilket blir vårt unika ID-nummer Returnerar unikt id. 9
2.2.8 Hämta data med XmlTextReader() Som tidigare nämnts erbjuder XmlTextReader-klassen en mer kraftfull lösning än XmlDocument. Här följer ett kort exempel på hur menyn (se Bild 1 under kapitlet Gränssnitt) kan genereras med XmlTextReader istället. // Skapar ny instans av StringBuilder-klassen. // Denna används för att länka samma data som ska skrivas ut. System.Text.StringBuilder sbxml = new System.Text.StringBuilder(); // Skapar ny instans av XmlTextReader-klassen och laddar in XML-dokument XmlTextReader xmlreader = new XmlTextReader("Databas.xml"); // Lägger till elementet menuitem till en objektvariabel object omenuitems = xmlreader.nametable.add("menuitem"); // Medan XML-filen läses igenom... while (xmlreader.read()) // Om nodtypen är ett element (<menuitem></menuitem>) så // skriv ut menyn if (xmlreader.nodetype == XmlNodeType.Element) // Om elementet är <menuitem> så skriv ut menyn if (xmlreader.name.equals(omenuitems)) sbxml.append("<li><a href=\"?id=\">").append(xmlreader.readstring()).append("</a></li>"); Som du kanske märker används inte XPath för att välja ut specifika element, därför tar vi ett sådant exempel också. Klassen som används är XPathDocument(). // Skapar ny instans av XPathDocument-klassen och laddar in XML-dokument XPathDocument doc = new XPathDocument(Server.MapPath("App_Data/Database.xml")); // Skapar ett read-only-objekt för navigering genom noder XPathNavigator nav = doc.createnavigator(); // Väljer nod (<menuitem>) utifrån XPath-uttrycket XPathNodeIterator iterator = nav.select("/pages/page/menuitem"); // Medan XML-filens läses... while (iterator.movenext()) // Klonar noden som valts (<menuitem>) till en variabel nav2. XPathNavigator nav2 = iterator.current.clone(); // Skriver ut menyn ulmenu.innerhtml += "<li><a href=\"?id\">" + nav2 + "</a></li>"; 10
3 Gränssnitt Bild 1 - Webbplatsen Bild 2 - Administrationens startsida Bild 3 - Hantera sidinnehåll 11
Bild 4 - Hantering av bilder 4 Tips Här kommer några snabba tips vid hantering av XML-filer. Vad ska applikationen användas till? Vad är viktigast: Enkelhet eller prestanda? Välj XML-klass utefter detta. Kolla först om XML-filen existerar innan du går vidare i din kod. Det undviker problem på vägen. Använd Try-Catch-block för att få robusthet i din applikation. Programmet Visual XPath (se kapitel 5) är ett bra hjälpmedel till att generera XPath-uttryck samt C#-kod. Använd XmlSortOrder för att sortera data. 5 Referenser 5.1 Webbplatser 5.1.1 XML http://www.w3.org/2001/10/glance/doc/howto http://www.w3.org/tr/xmlschema11-1/ Mjukvara för XPath queries: http://www.topxml.com/code/default.asp?p=3&id=v20031230175812&ms=120 &l=dotnet&sw=categ 5.1.2 XML och.net http://www.topxml.com/code/default.asp?p=1&ms=120&l=dotnet&sw=categ http://www.csharpcorner.com/code/2002/nov/viewwritexmlusingdataset.asp http://www.csharpfriends.com/articles/getarticle.aspx?articleid=309&page=2 http://support.microsoft.com/default.aspx?scid=kb;en-us;317666 http://support.microsoft.com/kb/307548 12
6 Bilagor XML-dokument (databas) <?xml version="1.0" encoding="iso-8859-1"?> <Pages> <page id="0"> <menuitem>meny 1</menuitem> <content>innehåll 1</content> <datetime>2006-10-03, 12:03:42</datetime> </page> <page id="1"> <menuitem>meny 2</menuitem> <content>innehåll 2</content> <datetime>2006-10-04, 11:47:01</datetime> </page> <page id="2"> <menuitem>meny 3</menuitem> <content>innehåll 3</content> <datetime>2006-10-05, 08:11:22</datetime> </page> </Pages> 13