Kompilacja i kompilatory Konwersja XML do REG Xml2reg Grzegorz Surdyka WGGiOŚ, Informatyka Stosowana IV rok, AGH 2010 Strona 1 z 10
Spis treści 1. Cel oraz podstawowe wiadomości o XML i REG... 3 2. O programie... 4 1. Podstawowe informacje... 4 2. Wykorzystane biblioteki i opis niektórych bloków programu... 5 1. Wczytywanie pliku... 5 2. Krótki opis klasy XmlTextReader, przykładowych metod oraz własności... 5 3. Opis niektórych bloków programu... 6 3. Przykłady działania programu... 7 3. Kod źródłowy programu... 8 Strona 2 z 10
1. Cel oraz podstawowe wiadomości o XML i REG Celem projektu było napisanie prostego konwertera plików w formacie XML do plików w formacie REG. XML 1 (ang. Extensible Markup Language) jest uniwersalnym językiem formalnym przeznaczonym do reprezentowania danych w strukturalizowany sposób. Niewątpliwą zaletą XML a jest to, że jest niezależny od platformy. Poprawny składniowo dokument XML powinien byd stworzony zgodnie z kilkoma zasadami: Powinien zawierad deklarację XML na początku pliku, wraz z atrybutem version oraz opcjonalnymi atrybutami encoding, standalone; Musi zawierac dokładnie JEDEN element głowny (tzw. root node); Każdy element musi zaczynad się znacznikiem początku elementu np. <ksiazka> oraz kooczyd się znacznikiem kooca elementu np. </ksiazka>; Nazwy złożone ze znaków alfanumerycznych (litery a-z, A-Z oraz cyfry 0-9) oraz ze znaków interpunkcyjnych ( _ ; - ;. ); Nazwy nie mogą zaczynad się od znaku łącznika -, kropki., dodatkowo nie mogą zaczynad się od ciągu znaków postaci xml wielkośd liter nie ma znaczenia; Elementy można zagnieżdżad (element wewnątrz nazywany jest dzieckiem child ); Każdy element może posiadad atrybuty np. książka ma swój numer ISBN - <ksiazka ISBN = 12345679 >; Informacje jak np. tytuł książki są zapisane pomiędzy znacznikiem początkowym, a koocowym <tytul>symfonia C++</tytul>; W danych, atrybutach oraz nazwach elementów nie mogą pojawid się znaki: <, & ; Można używad sekcji znakowych (np. z kodem html lub xml), które rozpoczynają się od <![CDATA[, a kooczą znacznikiem kooca ]]>; Można używad komentarzy pomiędzy znacznikami <! tu jest komentarz -->; <?xml version="1.0"?> <catalog> <book id="bk101"> <author>gambardella, Matthew</author> <title>xml Developer's Guide</title> <genre>computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>an in-depth look at creating applications with XML.</description> </book> <book id="bk102"> <author>ralls, Kim</author> <title>midnight Rain</title> <genre>fantasy</genre> <price>5.95</price> <publish_date>2000-12-16</publish_date> <description>a former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description> </book> </catalog> 1 za http://pl.wikipedia.org/wiki/xml Strona 3 z 10
Pliki REG 2 następująco: są to pliki rejestru systemu Windows. Budowa pliku REG wygląda Windows Registry Editor Version 5.00 [HKEY??\Klucz] @="dane wartości domyślnej" "xxx"="dane wartości ciągu o nazwie xxx" "yyy"=dword:0000006f "binarnawar"=hex:74,6f,20,64,6f,20,63,68,75,6a,61,20,6a,65,73,74,20,77,61,72,\ 74,6f,63,73,20,62,69,6e,61,72,6e,61 [HKEY??\inny_Klucz] @="dane wartości domyślnej innego klucza" "vvv"="dane wartości ciągu o nazwie vvv" "aaa"=dword:0000000c "bbb"=dword:0000000d "ccc"=dword:0000000e "zzz"=dword:0000000f Linijka Windows Registry Editor Version 5.00 jest obowiązkowa, informuje ona system o wersji programu Regedit. W nawiasach kwadratowych zapisana jest ścieżka do modyfikowanego klucza. W następnej linijce podana jest wartośd domyślna klucza, a w następnych nazwy oraz ich wartości dla danego klucza. 2. O programie 1. Podstawowe informacje Program xml2reg został napisany w języku C# przy użyciu środowiska Microsoft Visual C# 2008 Express Edition. Program jest aplikacją okienkową, umożliwia otwieranie, zapisywanie plików z poziomu menu, oraz dokonywanie konwersji z formatu XML do REG. Rys. 1 Zrzut ekranu z programu xml2reg z załadowanym plikiem XML 2 za http://metavirus.ovh.org/windows/regmodif.php Strona 4 z 10
2. Wykorzystane biblioteki 3 i opis niektórych bloków programu Program został napisany z wykorzystaniem biblioteki, która standardowo jest częścią języka C#, System.Xml. 1. Wczytywanie pliku Wczytywanie pliku odbywa się poprzez standardowa klasę StreamReader do Textbox a nr 1, do toolstripstatuslabel1 przekazujemy ścieżkę otwieranego pliku, która później posłuży jako ścieżka dla XmlTextReader a. xmlreader = new StreamReader(open_file); opened_file = xmlreader.readtoend(); textbox1.text = opened_file; toolstripstatuslabel1.text = open_file; xmlreader.close(); 2. Krótki opis klasy XmlTextReader, przykładowych metod oraz własności Klasa XmlTextReader służy do odczytu wprzód strumienia danych XML owych. Poruszanie się po danych XML owych opiera się na nawigacji po węzłach (node ach). Przykładowe metody klasy XmlTextReader: MoveToFirstAttribute() przechodzi do pierwszego atrybutu ; MoveToNextAttribute() przechodzi do następnego atrybutu; MoveToAttribute() przchodzi do konkretnego (po wcześniejszym ponumerowaniu) atrybutu; Read() odczytuje następny węzeł ze stream u; MoveToContent() sprawdza czy aktualny węzeł nie jest węzłem pustym, elementem zamykającym, sekcją znakową, jeśli jest (czyli nie ma zawartości), przechodzi do następnego węzła (do węzła content node ); IsStartElement() sprawdza czy aktualny content node jest elementem startowym bądź elementem pustym (przy użyciu MoveToContent()); Drugim ważnym elementem klasy XmlTextReader są jej własności (Property). Poniżej zostało przedstawione kilka z nich: NodeType zwraca typ węzła, gdzie typem może byd nazwa elementu, wartośd tekstowa, sekcja znakowa, komentarz, deklaracja XML; HasAttributes sprawdza czy są atrybuty; Name pobiera nazwę aktualnego node a; Value pobiera wartośd tekstową aktualnego node a; 3 całośd za http://msdn.microsoft.com/en-us/library/default.aspx Strona 5 z 10
3. Opis niektórych bloków programu Program do konwersji wykorzystuje ww. bibliotekę System.Xml. Program nie analizuje składni, odczytuje typy węzłów, ich wartości oraz zagnieżdżenia, po czym tak uzyskane informacje zapisuje do zmiennej typu String, odpowiednio formatuje i zapisuje do textbox2. W pierwszej kolejności stworzony zostaje obiekt typu XmlTextReader: XmlTextReader xmlreader = new XmlTextReader(toolStripStatusLabel1.Text); // czytanie pliku Stosujemy pętle while w celu przeczytania całego stream a XML owego. Dzięki zastosowaniu instrukcji switch przeskakujemy pomiędzy węzłami i wydobywamy nazwy oraz wartości poszczególnych node ów. while (xmlreader.read()) switch (xmlreader.nodetype) case XmlNodeType.Element: // kod case XmlNodeType.EndElement: // kod case XmlNodeType.Text: // kod default: Nazwy oraz wartości atrybutów zapisywane są w tablicy typu Hashtable o nazwie attributes. Jest to o tyle wygodne rozwiązanie że dane zapisywane są parami nazwa+wartośd. Wyłuskane nazwy węzłów zapisywane są do zmiennej sbnodename, która jest zmienną typu StringBuilder dzięki temu mamy możliwośd edytowania, w tym doklejania kolejnych znaków \\ dzięki wyrażeniu sbnodename.append( \\ ). Odpowiednio sformatowany ciąg sbnodename jest konwertowany do stringa metodą ToString() i zapisywany w zmiennej typu String - printnodename. Aktualny string jest przechowywany w zmiennej buffer ma to na celu zapobiec sytuacji wrzucania do textbox2 tych samych nazw jeśli trzeba zmienna buffer jest czyszczona i wtedy nic nie dorzucamy do textbox2. Powyższe rozwiązanie zastosowano także do eliminacji znaczników zamykających. Zmienne up, down, licznik oraz poziom odpowiadają za poprawne radzenie sobie programu z zagnieżdżeniami. Strona 6 z 10
3. Przykłady działania programu Plik XML przed konwersją: <?xml version="1.0" encoding="iso-8859-1"?> <!-- Edited by XMLSpy --> <CATALOG> <CD> <TITLE>Empire Burlesque</TITLE> <ARTIST>Bob Dylan</ARTIST> <COUNTRY> <STATE>NY</STATE> </COUNTRY> <COMPANY>Columbia</COMPANY> <PRICE>10.90</PRICE> <YEAR>1985</YEAR> </CD> <CD> <TITLE>Hide your heart</title> <ARTIST>Bonnie Tyler</ARTIST> <COUNTRY>UK</COUNTRY> <COMPANY>CBS Records</COMPANY> <PRICE>9.90</PRICE> <YEAR>1988</YEAR> </CD> </CATALOG> Plik wynikowy REG po konwersji z XML: Windows Registry Editor Version 5.00 [CATALOG] [CATALOG\CD] "TITLE"=Empire Burlesque "ARTIST"=Bob Dylan [CATALOG\CD\COUNTRY] "STATE"=NY [CATALOG\CD] "COMPANY"=Columbia "PRICE"=10.90 "YEAR"=1985 [CATALOG\CD] "TITLE"=Hide your heart "ARTIST"=Bonnie Tyler "COUNTRY"=UK "COMPANY"=CBS Records "PRICE"=9.90 "YEAR"=1988 Strona 7 z 10
3. Kod źródłowy programu Kompilacja i kompilatory: xml2reg Niektóre zakomentowane linie odnoszą się do wersji konsolowej programu, wersji pierwotnej, miały na celu zobrazowanie, które elementy są usuwane / dodawane do stringa, oraz w którym miejscu aktualnie znajduje się XmlTextReader. // Konwerter plikow XML do formatu REG // (C) Grzegorz Surdyka, Informatyka Stosowana, IV rok, AGH 2010 // using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; using System.Xml; using System.Collections; namespace xml2reg public partial class Form1 : Form string opened_file; string open_file; string clear = ""; string elname1; string elname2; string buffer = ""; int dlugosc1; int dlugosc2; string printnodename; char[] dowywalenia = '\\' ; int down; int up; int poziom; int licznik; public Form1() InitializeComponent(); private void zamknijtoolstripmenuitem_click(object sender, EventArgs e) Close(); private void otwórztoolstripmenuitem_click(object sender, EventArgs e) // otwarcie pliku i wrzucenei do textboxa StreamReader xmlreader; openfiledialog1.showdialog(); open_file = openfiledialog1.filename; xmlreader = new StreamReader(open_file); opened_file = xmlreader.readtoend(); textbox1.text = opened_file; toolstripstatuslabel1.text = open_file; xmlreader.close(); textbox2.text = clear; buffer = ""; down = 0; up = 0; Strona 8 z 10
poziom = 0; licznik = 0; private void xmlregtoolstripmenuitem_click(object sender, EventArgs e) StringBuilder sbnodename = new StringBuilder(); XmlTextReader xmlreader = new XmlTextReader(toolStripStatusLabel1.Text); // czytanie pliku textbox2.appendtext("windows Registry Editor Version 5.00" + Environment.NewLine); while (xmlreader.read()) switch (xmlreader.nodetype) case XmlNodeType.Element: Hashtable attributes = new Hashtable(); down++; poziom = poziom + 1; //textbox2.appendtext("[element] W DOL [DOWN]= " + down + " POZIOM = " + poziom + Environment.NewLine); //textbox2.appendtext("[element] W GORE [UP]= " + up + " POZIOM = " + poziom + Environment.NewLine); textbox2.appendtext(buffer); sbnodename.append(xmlreader.name); elname2 = xmlreader.name; sbnodename.append("\\"); // dorzucamy \ pomiedzy galezie wywalamy ost. znak \ Environment.NewLine; printnodename = sbnodename.tostring().trimend(dowywalenia); // buffer = Environment.NewLine + "[" + printnodename + "]" + if (xmlreader.hasattributes) textbox2.appendtext(environment.newline + "[" + printnodename + "]" + Environment.NewLine); // gotowa galaz // sprawdzamy czy sa atrybuty, jak tak to do hashatable'a i wypisujemy for (int i = 0; i < xmlreader.attributecount; i++) xmlreader.movetoattribute(i); attributes.add(xmlreader.name, xmlreader.value); ///Console.Write("\"" + xmlreader.name + "\"=" + xmlreader.value + Environment.NewLine); textbox2.appendtext("attr_\"" + xmlreader.name + "\"=" + xmlreader.value + Environment.NewLine); textbox2.appendtext(environment.newline + "[" + printnodename + "]" + Environment.NewLine); // gotowa galaz buffer = ""; case XmlNodeType.EndElement: ///Console.WriteLine(" USUN: " + xmlreader.name); // nazwa usuwanej galezi dlugosc1 = sbnodename.length; // dlugosc nazwy node'a elname1 = xmlreader.name.tostring(); // elementu do wyrzucenia dlugosc2 = elname1.length + 1; // dlugosc elem. do wyrzucenia sbnodename.remove(dlugosc1 - dlugosc2, dlugosc2); up++; poziom = poziom - 1; licznik++; //textbox2.appendtext("[endelement] W DOL [DOWN]= " + down + " POZIOM = " + poziom + Environment.NewLine); //textbox2.appendtext("[endelement] W GORE [UP]= " + up + " POZIOM = " + poziom + " LICZNIK = " + licznik + Environment.NewLine); Strona 9 z 10
if (up == 1 && licznik == 4 up == 2 && poziom == 2 && licznik == 4) printnodename = sbnodename.tostring().trimend(dowywalenia); // wywalamy ost. znak / textbox2.appendtext(environment.newline + "[" + printnodename + "]" + Environment.NewLine); // gotowa galaz licznik = 0; case XmlNodeType.Text: buffer = ""; up--; textbox2.appendtext("\"" + elname2 + "\"" + "=" + xmlreader.value + Environment.NewLine); default: private void zapisztoolstripmenuitem_click(object sender, EventArgs e) String save_file; if (savefiledialog1.showdialog() == DialogResult.OK) save_file = savefiledialog1.filename; StreamWriter saved_file = new StreamWriter(save_file); saved_file.writeline(textbox2.text); saved_file.close(); Strona 10 z 10