Format XML jest obecnie standardem zapisu danych. Jego uniwersalność i wszchstronność sprawia, że jest to format wykorzystywany niemal wszędzie. Elastyczność tego formatu oraz fakt, że może zawierać informacje o swojej strukturze sprawiło, że bardzo szybko został przyjęty przez programistów oraz twórców stron internetowych. Warto zapoznać się ze strukturą budowy dokumentu XML, gdyż Microsoft bardzo silnie zintegrował swoje narzędzia z tym właśnie formatem. Pisząc programy w Visual Studio, nawet nieświadomie będziesz używał XML w swoich projektach.
Dokument XML składa się z:
• nagłówka,
• komentarzy,
• elementu głównego,
• elementów,
• atrybutów.
Nagłówek dokumentu XML nie jest wymagany, lecz warto go używać, ze względu na kompatybilność. Nagłówek ma następującą postać:
<?xml version="1.0"?>
Opcjonalnie, można dołączyć do nagłówka format kodowania znaków, co umożliwi ich poprawny odczyt:
<?xml version="1.0" encoding=”utf-8” ?>
Komentarze w XML, służą podobnie jak w HTML i innych językach programowania do opisywania określonych wierszy. Komentarze mogą zawierać dowolne treści.
<!— Tak można komentować w XML -->
Główny element – korzeń
Każdy dokument w formacie XML zawiera jeden jeden element główny. W elemencie tym, są zawarte wszystkie inne zagnieżdżone elementy. Dlatego też, dokumenty XML mają strukturę zwaną drzewiastą lub hierachiczną.
Elementy dokumentu
Elementy znajdują się wewnątrz głównego elementu (korzenia). Element składa się ze znaczkina początkowego oraz końcowego, innych zagnieżdżonych dokumentów lub łańcucha znaków.
Atrybuty dokumentu
Każdy element w dokumencie XML może zawierać atrybuty opisane w znaczniku początkowym. Jest to po prostu jeszcze jeden sposób na przekazanie danych dotyczących elementu. Atrybut musi stosować się do szablonu nazwa = „wartość”.
Oto kod przykładowego dokumentu XML:
<?xml version="1.0" encoding="utf-8" ?> <!-- Baza zawierająca adresy --> <Adresy> <Adres> <Imie>Leszek</Imie> <Nazwisko>Klich</Nazwisko> <Telefon>000 000-00-00</Telefon> </Adres> <Adres> <Imie>Jan <Nazwisko>Kowalski</Nazwisko> <Telefon/> </Adres> </Adresy>
Elementem głównym (korzeniem) jest w naszym przypadku . Elementami są dwa „rekordy” zawierające atrybuty Imię, Nazwisko oraz Telefon. Warto nadmienić, że XML rozpoznaje wielkość liter. Tak więc wpisanie spowoduje błędy w odczycie dokumentu. W dokumentach XML należy także zadbać o zamykanie każdych znaczników. W przeciwnym wypadku dokument będzie niepoprawny składniowo. Wypełnianie elementu atrybutem jest opcjonalne. Warto jednak wiedzieć, że jeśli w jedym elemencie występuje znacznik , to jeśli nie zamierzamy wprowadzać go w kolejnym elemencie, nalzęy umieścić znacznik pusty – , co widać w przykładowych dokumentach. Odpowiednikiem takiego zapisu jest . Dodatkowo, do elementu można dopisać atrybut przy czyli . Oczywiście wcale nie musi to być wartość numeryczna. Atrybuty mogą być pomocne na przykład podczas przeszukiwania bazy.
<?xml version="1.0" encoding="utf-8" ?> <!-- Baza zawierająca adresy --> <Adresy> '<'Adres Id="1"> <Imie>Leszek</Imie> <Nazwisko>Klich'</Nazwisko> <Telefon>000 000-00-00</Telefon> </Adres> <Adres Id="2"> <Imie>Jan</Imie> <Nazwisko>Kowalski</Nazwisko> <Telefon/> </Adres> </Adresy>
W dokumencie XML bardzo ważną zasadą poprawności formatowania jest kolejność otwierania i zamykania znaczników. Węzły muszą być otwierane i zamykane w tej samej kolejności.
<Adres> <Imie>Leszek</Imie> <Nazwisko>Klich</Nazwisko> <telefon>000 000-00-00 </Adres> </telefon>
Powyższy zapis jest błędny, gdyż nie zachowano kolejności zamknięcia znaczników. Warto zwrócić na to uwagę, podczas tworzenia dokumentów XML.
Węzły w dokumencie XML można dowolnie zagnieżdżać. Oczywiście, jak już wspomnieliśmy, wszelkie węzły muszą znajdować się wewnątrz znaczników korzenia. Spróbujmy dla przykładu opisać dokumentem XML zwierzęta.
Podział będzie wyglądał tak:
Zapiszmy więc dokument w formacie XML o strukturze, jak na Rysunek 15 Struktura dokumentu zwierzęta. Pamiętajmy o zasadach formatowania dokumentów, zamykaniu i kolejności znaczników.
<?xml version="1.0" encoding="utf-8" ?> <!-- Zwierzęta --> <zwierzeta> <domowe> <psy> <rasa>Owczarek niemiecki</rasa> <rasa>Doberman</rasa> <rasa>Labrador</rasa> <rasa>Pinczer</rasa> <Terier> <Terier>Krótkowłosy</Terier> <Terier>długowłosy</Terier> </Terier> </psy> <koty> <rasa>Pers</rasa> <rasa>Syjamski</rasa> <rasa>Syberyjski</rasa> <rasa>Burmski</rasa> </koty> </domowe> <dzikie> <zwierz>Słoń</zwierz> <zwierz>Żyrafa</zwierz> Hipopotam</zwierz> Tygrys'</zwierz> </dzikie> </zwierzeta>
Zapisz dokument XML na dysku pod dowolną nazwą i uruchom go za pomocą przeglądarki internetowej. Jeśli nie popełniłeś błędów podczas przepisywania, przeglądarka otworzy dokument w postaci:
Obsługa XML w Visual Studio C#
Mając już przygotowany plik dokumentu XML, zapewne chciałbyś teraz móc go odczytać z poziomu j ęzyka C#. W tej części zbudujemy bazę danych, w której dane będziemy przechowywać w pliku XML.
Tworzymy bazę danych XML
Na wstępie należy zaznaczyć, że znacznie lepszym sposobem tworzenia baz danych jest wykorzystanie serwera MS SQL Server. Jednak jako przykład, napiszemy aplikację, która udowoni, że XML także nadaje się do tworzenia baz danych. Jako przykładową aplikację utworzymy bazę adresową. Aplikacja korzystać będzie z danych zapisanych w pliku XML. Możliwe będzie dodawanie, usuwanie oraz edycja rekordó w bazie. W każdej chwili będzie można podglądnąć strukturę pliku XML dla celów edukacyjnych. Gotowy uruchomiony program widać na poniższym rysunku:
Budowanie nowej aplikacji zaczynamy od utworzenia nowego projektu. Jako typ nowego projektu wybieramy Aplikacja Windows Forms i zapisujemy jako BazaXML. Następnie należy zmienić rozmiar formy głównej na odpowiedni. Zmień właściwości formy: MaximizeBox na false, StartPosition – CenterScreen, Text na Baza kontaktowa. Dodaj do formatki wymagane komponenty jak na rysunku poniżej, nie zapominając o dodaniu kontrolki dataSet, który będzie realizował połączenie z danymi.
Jeśli projekt formularza jest gotowy, możemy przystąpić do programowania. Przełącz się w tryb kodu – F7 i w sekcji uses dopisz niezbędne biblioteki:
using System.Xml; using System.Xml.XPath; using System.IO;
Biblioteki umożliwiają korzystanie z plików XML oraz urządzeń wejścia wyjścia. Deklaracje można zrobić teraz, lub podczas programowania zdarzeń. Lepiej jednak dopisać je i mieć to za sobą. Unikniewy wówczas kłopotów z pisaniem kodu. Teraz musimy zadeklarować zmienną FILE_NAME, która będzie przechowywać nazwę pliku oraz napisać metodę, czytającą dane. Struktura początkowa programu wygląda tak:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } //wspólne dane const string FILE_NAME = "przyklad.xml"; //sciezka do pliku XML public void czytajDane() { try { DataSet _dataSet = new DataSet(); _dataSet.ReadXml(FILE_NAME); this.dataGrid1.DataSource = _dataSet; this.dataGrid1.DataMember = "Dane"; } catch { //tworzymy nowa bazę XML - jeżeli nie istnieje MessageBox.Show("Brak danych do odczytu", "Informacja"); string nazwaPliku = FILE_NAME; XmlTextWriter _xmlTextWriter = new XmlTextWriter(nazwaPliku, System.Text.Encoding.UTF8); _xmlTextWriter.WriteStartDocument(); _xmlTextWriter.WriteStartElement("dataSet"); _xmlTextWriter.WriteStartElement("Dane"); _xmlTextWriter.WriteElementString("Imie", "zmień dane"); _xmlTextWriter.WriteElementString("Nazwisko", "zmień dane"); _xmlTextWriter.WriteElementString("Adres", "zmień dane"); _xmlTextWriter.WriteElementString("KodPocztowy", "zmień dane"); _xmlTextWriter.WriteElementString("Miasto", "zmień dane"); _xmlTextWriter.WriteElementString("Telefon", "zmień dane"); _xmlTextWriter.WriteElementString("Email", "zmień dane"); _xmlTextWriter.WriteEndElement(); _xmlTextWriter.WriteEndDocument(); _xmlTextWriter.Flush(); _xmlTextWriter.Close(); czytajDane(); MessageBox.Show("Brak bazy XML. Utworzono nową bazę.", "Informacja"); } } //koniec funkcji czytaj dane
Oto pełny kod źródłowy aplikacji baza XML
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; // dołączenie niezbędnych bibliotek using System.Xml; using System.Xml.XPath; using System.IO; namespace WindowsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } //wspólne dane - nazwa pliku bazy const string FILE_NAME = "przyklad.xml"; //sciezka do bazy public void czytajDane() { try { DataSet _dataSet = new DataSet(); _dataSet.ReadXml(FILE_NAME); this.dataGrid1.DataSource = _dataSet; this.dataGrid1.DataMember = "Dane"; } catch { //tworzymy nową baze XML MessageBox.Show("Brak danych do odczytu", "Informacja"); string nazwaPliku = FILE_NAME; XmlTextWriter _xmlTextWriter = new XmlTextWriter(nazwaPliku, System.Text.Encoding.UTF8); _xmlTextWriter.WriteStartDocument(); _xmlTextWriter.WriteStartElement("dataSet"); _xmlTextWriter.WriteStartElement("Dane"); _xmlTextWriter.WriteElementString("Imie", "zmień dane"); _xmlTextWriter.WriteElementString("Nazwisko", "zmień dane"); _xmlTextWriter.WriteElementString("Adres", "zmień dane"); _xmlTextWriter.WriteElementString("KodPocztowy", "zmień dane"); _xmlTextWriter.WriteElementString("Miasto", "zmień dane"); _xmlTextWriter.WriteElementString("Telefon", "zmień dane"); _xmlTextWriter.WriteElementString("Email", "zmień dane"); _xmlTextWriter.WriteEndElement(); _xmlTextWriter.WriteEndDocument(); _xmlTextWriter.Flush(); _xmlTextWriter.Close(); czytajDane(); MessageBox.Show("Brak bazy XML. Utworzono nową bazę.", "Informacja"); } } //koniec czytaj dane private void Form1_Load(object sender, EventArgs e) { czytajDane(); } private void button5_Click_1(object sender, EventArgs e) { StreamReader sr = File.OpenText(FILE_NAME); String input; input = sr.ReadToEnd(); sr.Close(); textBox6.Text = input; XmlTextReader reader = new XmlTextReader(FILE_NAME); XmlDocument doc = new XmlDocument(); doc.Load(reader); reader.Close(); XmlNode currNode; XmlDocumentFragment docFrag = doc.CreateDocumentFragment(); if (textBox1.Text == "") //imie { MessageBox.Show("Uzupełnij pole: Imię", "Informacja"); } else if (textBox2.Text == "") //nazwisko { MessageBox.Show("Uzupełnij pole: Nazwisko", "Informacja"); } else { docFrag.InnerXml = "<Dane>" + "<Imie>" + textBox1.Text + "</Imie>" + "<Nazwisko>" + textBox2.Text + "</Nazwisko>" + "<Adres>" + textBox3.Text + "</Adres>" + "<KodPocztowy>" + textBox4.Text + "</KodPocztowy>" + "<Miasto>" + textBox5.Text + "</Miasto>" + "<Telefon>" + textBox7.Text + "</Telefon>" + "<Email>" + textBox8.Text + "</Email>" + "</Dane>"; //czysciymy pola textBox1.Text = ""; textBox2.Text = ""; textBox3.Text = ""; textBox4.Text = ""; textBox5.Text = ""; textBox7.Text = ""; textBox8.Text = ""; } currNode = doc.DocumentElement; currNode.InsertAfter(docFrag, currNode.LastChild); doc.Save(FILE_NAME); czytajDane(); } //dodaj koniec private void button9_Click(object sender, EventArgs e) { DialogResult result = MessageBox.Show("Skasować rekord?", "Wybierz opcję", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3); if (result == DialogResult.Yes) { try { int z = dataGrid1.CurrentRowIndex; //zwraca aktualna pozycje kursora int v = z + 1; XmlTextReader reader = new XmlTextReader(FILE_NAME); XmlDocument doc = new XmlDocument(); doc.Load(reader); reader.Close(); XmlNode cd; XmlElement root = doc.DocumentElement; //xPath cd = root.SelectSingleNode("/dataSet/Dane[position()=" + v + "]"); root.RemoveChild(cd); doc.Save(FILE_NAME); czytajDane(); } catch { MessageBox.Show("Brak rekordów do skasowania", "Informacja"); } } } //koniec kasuj private void button2_Click(object sender, EventArgs e) { StreamReader sr = File.OpenText(FILE_NAME); String input; input = sr.ReadToEnd(); sr.Close(); textBox6.Text = input; } //koniec odswiez private void button3_Click_1(object sender, EventArgs e) { try { XmlTextReader reader = new XmlTextReader(FILE_NAME); XmlDocument doc = new XmlDocument(); doc.Load(reader); reader.Close(); int z = dataGrid1.CurrentRowIndex; int v = z + 1; XmlNode oldCd; XmlElement root = doc.DocumentElement; oldCd = root.SelectSingleNode("/dataSet/Dane[position()=" + v + "]"); //stary wezel XmlElement newCd = doc.CreateElement("Dane"); //nowy wezel newCd.InnerXml = "<Imie>" + textBox1.Text + "</Imie>" + "<Nazwisko>" + textBox2.Text + "</Nazwisko>" + "<Adres>" + textBox3.Text + "</Adres>" + "<KodPocztowy>" + textBox4.Text + "</KodPocztowy>" + "<Miasto>" + textBox5.Text + "</Miasto>" + "<Telefon>" + textBox7.Text + "</Telefon>" + "<Email>" + textBox8.Text + "</Email>"; root.ReplaceChild(newCd, oldCd); doc.Save(FILE_NAME); czytajDane(); } catch { this.DialogResult = DialogResult.Cancel; } } //koniec edycja public void wczytajDane() { XPathDocument doc; XPathNavigator nav; XPathExpression expr; XPathNodeIterator iterator; int z = dataGrid1.CurrentRowIndex; int v = z + 1; doc = new XPathDocument(FILE_NAME); nav = doc.CreateNavigator(); expr = nav.Compile("/dataSet/Dane[position()=" + v + "]"); iterator = nav.Select(expr); while (iterator.MoveNext()) { XPathNavigator nav2 = iterator.Current.Clone(); nav2.MoveToFirstChild(); //pierwszy rekord textBox1.Text = nav2.Value; nav2.MoveToNext(); // ..i nastepny textBox2.Text = nav2.Value; nav2.MoveToNext(); // ..i nastepny textBox3.Text = nav2.Value; nav2.MoveToNext(); // ..i nastepny textBox4.Text = nav2.Value; nav2.MoveToNext(); textBox5.Text = nav2.Value; nav2.MoveToNext(); textBox7.Text = nav2.Value; nav2.MoveToNext(); textBox8.Text = nav2.Value; } } //koniec wczytajDane private void dataGrid1_Click(object sender, EventArgs e) { //dodana akcja. Kliknięcie powoduje odswiezenie danych w polach tekstowych wczytajDane(); } } }