XML w Delphi
Deti
1 Wstęp
2 Budowa prostego pliku XML
3 Odczyt w Delphi
4 Zapis
5 Usuwanie
6 Atrybuty
7 Tworzenie drzewka
8 Tworzenie interfejsów.
9 Podsumowanie
W artykule tym zajmiemy się wykorzystaniem XMLa w aplikacjach. Ograniczę się tu tylko konkretnie do delphi - jak wiemy XML ma wszechstronne zastosowanie - również w projektowaniu stron www - temat ten nie będzie jednak poruszany. Na początek zajmiemy się podstawami czyli zapisem / odczytem informacji w tym formacie, ich dodawaniem, usuwaniem itd, a później conieco na temat tworzenia interfejsów danych w XML.
Wstęp
XML to skrót od eXtensible Markup Language - rozszerzalny język znaczników. Krótko mówiąc - jest to format do przechowywania "różnych" danych. Dlaczego jest tak dobry? Otóż chodzi przede wszystkim o prostotę i wygodę użytkowania (zapisu/odczytu informacji). Osoby znające chociaż podstawy języka HTML bedą czuli się tu jak u siebie w domu - ponieważ XML oparty jest o znaczniki. Dokładną budowę poznasz w swoim czasie. Skorzystajmy od razu z przykładu: powiedzmy, że chcemy zapisać pewien rekord do pliku w celu późniejszego odczytu. Sposobów na to jest co niemiara: ot chociażmy do plików typowanych lub strumienia. Jednakże powyższe rozwiązania nie są zbyt wygodne dla użytkownika - bowiem plików tych nie jest łatwo podejrzeć, sprawdzić ich aktualną zawartość. Tak naprawdę nie wiemy jak wygląda cały ten zapis. Ostatecznie można zapisać rekord do pliku tekstowego, jednak rozwiązanie takie jest już trudniejsze, a poza tym - mniej bezpieczne i nieprofesjonalne. I tu wreszcie przychodzi nasz XML. Rekord nasz można zapisać w pliku .xml, który jest co prawda zwykłym plikiem tekstowym - ale ma określoną budowę. XML jest stosunkowo łatwy w obsłudze, a dzięki temu, że jest to zwykły plik tekstowy - można zawsze podejrzeć aktualną zawartość chociażby w zwykłym notatniku. Ale czy to wszystko? - oczywiście nie.. W XMLu nie są konieczne żadne deklaracje typów, budowy rekordów itd - wszystko jest praktycznie zawarte w samym zapisie - sam zapis decyduje o budowie danych. Delphi dodatkowo posiada mechanizmy pozwalające na tworzenie dynamicznych interfejsów - tworzenia metod i właściwości na podstawie danej struktury XML - i to wszystko tworzy się automatycznie ..
Budowa prostego pliku XML
Spójrzmy na przykładowy plik XML:
<?xml version="1.0" encoding="UTF-8"?>
<Owoce>
<Poziomka>
<cena>22.65</cena>
<dostawca>Niemcy</dostawca>
</Poziomka>
<Gruszka>
<cena>12.75</cena>
<dostawca>Polska</dostawca>
</Gruszka>
<Banan>
<cena>12.75</cena>
<dostawca>Portugalia</dostawca>
</Banan>
</Owoce>
Już na pierwszy strzał widzimy podobieństwo z HTML'em - tutaj jednak nie mamy ograniczenia co do tagów - sami bowiem je określamy i same tagi stanowią informacje. Pierwsza linijka stanowi zwykły nagłówek XML, mówi nam o wersji oraz kodowaniu pliku (w naszym przypadku jest to UTF-8). Szersze informacje na ten temat znajdziecie tutaj: http://4programmers.net/file.php?id=1239. My zajmijmy się czymś praktycznym. Nasz plik XML posiada jak widać swoistą wypiskę owoców - są to przykładowe 3 elementy: poziomka, gruszka, banan. Każde z nich posiada informacje o cenie i dostawcy. Jak widzimy - sami możemy zapisywać dalsze informacje, dodać pozostałe owoce. Co więcej - sami możemy dodać informacje o owocu - np. ilość. To my decydujemy o budowie zapisu - już w samym zapisie. Tutaj nie ma żadnych deklaracji. Spójrzmy jak to wygląda z poziomu Delphi: głownym elementem jest "Owoce" -głowny element może być tylko jeden w XMLu - nie możemy do tego pliku dodać nowego elementu głownego, np. "Warzywa". Element ten posiada 3-ech potomków (tzw: Child Nodes): Poziomka, Gruszka, Banan. Każde z nich posiada po 2 potomki: cena i dostawca. Każdy element (w naszym pliku jest ich 10 - licząc głowny) to element w zapisie XML (IXMLNode) stanowiący interfejs. Każdy z nich posiada zatem swoje metody, dzięki którym możemy dokonywać modyfikacji zapisu. IXMLNode na siłę można porównać do klasy TTreeNode stanowiącej element w komponencie TTreeView - obiekty te są podobne. Bezpośrednim "rodzicem" dla poszczególnych owoców jest element "Owoce". W naszym przypadku mamy do czynienia ze swoistą hierarchią elementów. "Owoce" są tym dla innych elementów, czym TObject dla innych komponentów - zmieniają się jedynie relacje. U nas są to relacje - " rodzic - syn" w odpowiednim tego słowa znaczeniu - w drugim przypadku natomiast relacja znaczyła położenie klasy w hierarchii (macierzysta - potomna). Warto zwrócić uwagę na różnice pomiędzy elementami: "cena" oraz "Gruszka". Gruszka posiada jedynie swoje elementy - dzieci, czyli "cena" i "dostawca", natomiast element "cena" posiada wartość, zapisaną pomiędzy swoimi tagami. Taki element posiada właściwość "Text", ale o tym później.
Gdyby z jakiejś przyczyny zaszła potrzeba postawienia pustego elementu, można to zrobić na 2 posoby. Tak:
<Pusty></Pusty>
.. lub tak:
<Pusty/>
Obie wersje są jak najbardziej poprawne i stanowią pusty element.
Odczyt w Delphi
Okej - dość tych bzdur - czas zacząć cześć praktyczną. Aby móc obsługiwać XML trzeba być w posiadaniu odpowiedniego parsera tekstu, który z gąszczu znaczników zrobi zrozumiałą dla nas strukturę danych. Taki parser posiada między innymi komponent TXMLDocument (znajduje się on w zakładce Internet). Można go śmiało postawić na głowną formę naszego programu - jest to komponent nie wizualny. Jak widzimy posiada on parę opcji i właściwości godnych omówienia. Właściwość Active raczej nie powinna wzbudzać pytań. Jak wiemy zapis do XMLa opiera się do zwykłego zapisu tekstu, zatem od razu rzuca nam się w oczy właściwość XML - właśnie tym będąca. Musimy zdawać sobie sprawę z tego, że operacje, o których mówiłem są przeprowadzane na tekście - zatem nie jest konieczny bezpośredni zapis do pliku. Ale przełączając na True opcje doAutoSave- właśnie tak uczynimy - i wszelkie zmiany będą natychmiastowo, automatycznie zapisywane do pliku - określonego w FileName. NodeIndentStr określa typ wcięć stosowanych w zapisie - ja preferuje podwójne spacji, niektórzy poczwórne kwestia wygody. Polecam również przełączenie na True opcji doNodeAutoIndent - dzięki temu wcięcia będą robione automatycznie.
Zapiszmy nasz przykładowy zapis do pliku test.xml na dysku C. Dla prostoty ćwiczeń komponent nasz nazwijmy po prostu "XML". A tak wygląda odczyt informacji:
XML.LoadFromFile('c:\test.xml');
XML.Active := True;
Teraz możemy już odczytać nasze dane. Jak wspomniałem jedynym elementem głownym jest "Owoce", sprawdźmy:
ShowMessage(XML.DocumentElement.LocalName);
Tutaj bardzo istotną rzeczą jest DocumentElement, który stanowi głowny element "Owoce", właściwość LocalName zawiera właśnie ten tekst. W celach czysto edukacyjnych do odczytu informacji wykorzystamy komponent memo. Oto przykład jak odczytać wszystkie owoce z naszych danych:
var
i: Integer;
begin
for i := 0 to XML.DocumentElement.ChildNodes.Count - 1 do
Memo1.Lines.Add(XML.DocumentElement.ChildNodes[i].LocalName);
end;
ChildNodes.Count stanowi ilość elementów potomnych - zresztą od razu rzuca się w oczy podobieństwo do standardowych metod VCL. Ostatnie w hierarchii elementy jak "cena" posiadają właściwość Text - stanowiącą konretną wartość. Takiej właściwości nie ma "Owoce" czy też "Poziomka". Decyduje o tym właściwość IsTextElement. Jak widzimy wszystko opiera się o interfejs IXMLNode.
Zapis
Zapis nowych elementów do XMLa można przeprowadzać za pomocą właściwości / metody, IXMLNode. Jest ich dość znaczna ilość -opis wszystkich znajduje się w Helpie. Aby dodać nowy owoc do naszej listy, zrobimy to tak:
var
XMLNode: IXMLNode; // owoc
XMLValues: IXMLNode; // 'parametry' owocu
begin
XMLNode := XML.DocumentElement.AddChild('Malina'); // Dodanie nowego owocu
XMLValues := XMLNode.AddChild('cena'); // Dodanie 'cena' dla maliny
XMLValues.Text := '54.32'; // wartość ceny
XMLValues := XMLNode.AddChild('dostawca'); // Dostawca
XMLValues.Text := 'Polska'; // Wartość
end;
Oczywiście, aby zoabczyć wyniki trzeba zapisać XML do jakiegoś pliku (...SaveToFile). AddChild jest funkcją - nie procedurą - dlatego możmemy przypisać do niej konkretny element. Zwróćmy uwagę jak są dodawane cena i dostawca: bezpośrednio do XMLNode. Dodawanie elementów nie jest chyba specjalnie skomplikowane i nie powinno sprawiać problemów..
Usuwanie
Spróbujmy usunąć któryś z owoców .. np. banan. Do tego posłuży nam zwykłe polecenie Delete.
XML.DocumentElement.ChildNodes.Delete('Banan');
Pamiętajmy, że wielkość liter nie jest bez znaczenia, gdybym napisał 'banan' - funkcja nie zadziała by. Gdy się przyjrzymy funkcji Delete widać, że jest ona przeciążona - zamiast nazwy możesz równie dobrze wpisać liczbę - wtedy usunięty zostanie element o tym właśnie index'ie. Funkcja ta usunie również wszelkie elementy potomne do 'Banan'.
Warto w tym miejscu zwrócić również uwagę na właściwość ChildNodes, która zawiera szereg bardzo przydatnych metod, takich jak Clear, FindNode, Count, Remove, Insert, IndexOf, First, Last i cała gama innych funkcji, które doskonale ułatwiają prace przy XMLu - i stanowią swoiste operacje na liście elementów, gdyż ChildNodes zawiera właśnie listę elementów bezpośrednio potomnych.
Atrybuty
Jeżeli wszystkie powyższe opisy, metody będą jeszcze niewystarczające - XML daje możliwość dodatkowych opisów danych - są nimi właśnie atrybuty. Dzięki nim szybko i łatwo można opisać dowolny element na swój własny sposób - a sam sposób zapisu atrybutów również jest prosty. Zmodyfikujmy nieco nasz przykład:
<?xml version="1.0" encoding="UTF-8"?>
<Owoce>
<Poziomka kolor="czerwony">
<cena>22.65</cena>
<dostawca>Niemcy</dostawca>
</Poziomka>
<Gruszka kolor="zielony">
<cena>12.75</cena>
<dostawca>Polska</dostawca>
</Gruszka>
<Banan kolor="zolty">
<cena>12.75</cena>
<dostawca>Portugalia</dostawca>
</Banan>
</Owoce>
Najpierw drobne wyjaśnienie: jak widzisz nie wprowadziłem polskich liter, zamiast "żółty" jest "zolty", gdyż jest zadeklarowane kodowanie UTF-8 - dla celów edukacyjnych nie wprowadziłem "krzaczków", żeby nie odstraszyć 'czytelnika' :) Jednak celowo zastosowałem UTF-8, gdyż XML wykorzystywany jest ze względu na uniwersalność, bądźmy więc uniwersalni i dajmy możliwość odczytu naszych informacji na całym świecie :)
W naszym przypadku każdy owoc został opatrzony atrybutem "kolor". Atrybuty wprowadzamy podobnie jak w CSS, czyli:
właściwość="wartość"
Tutaj mamy jednak większe możliwości od CSS - sami bowiem określamy właściwości, ich nazwy. Tutaj praktycznie nie ma ograniczeń. Aby dodać więcej niż jedna właściwość - oddzielamy je spacją (nie jak w CSS - średnikiem). Wartości atrybutów zapisujemy w cudzysłowiu. Zmieńmy sobie zatem kolor jakiegoś elementu:
XML.DocumentElement.ChildNodes[1].SetAttributeNS('kolor', '', 'czarny');
Powyższy kod zmieni nam akurat gruszkę na czarną - w życiu takiej nie widziałem - ale niech już będzie :]. Pierwszy parametr określa właściwość, drugi - tzw. NameSpaceURI - link do nowo tworzonego elementu - u nas zastosowałem pusty łańuch, trzeci natomiast - samą wartość. Znając atrybuty można przekształcić nasz plik do takiej postaci:
<?xml version="1.0" encoding="UTF-8"?>
<Owoce>
<Poziomka kolor="czerwony" cena="22.65" dostawca="Niemcy"/>
<Gruszka kolor="zielony cena="12.75" dostawca="Polska"/>
<Banan kolor="zolty" cena="12.75" dostawca="Portugalia"/>
</Owoce>
Czyli wprowadzjąc dane operując na samych atrybutach.
A tym sposobem możemy na przykład wyświetlić atrybut:
memo1.Lines.Add(XML.DocumentElement.ChildNodes[1].Attributes['kolor']);
Są to oczywiście tylko pojedyncze przykłady zastosowań - nie sposób opisać wszystkie metod dostępnych podczas pracy z XML'em. Ale jak widać ich ilość pokazuje nam potęgę XMLa w różnych zastosowaniach.
Tworzenie drzewka
XML jest łatwym narzędziem do zapisu hierarchii elementów (tzw. drzewka). Spróbujmy zapisać budowę drzewka z komponentu TreeView. Cała procedura będzie oczywiście rekurencyjna..
procedure Listing(ATreeNode: TTreeNode; AIXMLNode: IXMLNode);
var
i: Integer;
begin
if not ATreeNode.HasChildren then
Exit;
for i := 0 to ATreeNode.Count - 1 do
Listing(ATreeNode.Item[i], AIXMLNode.AddChild(ATreeNode.Item[i].Text));
end;
Po chwili zastanowienia dojdziemy do zrozumienia całego kodu. Procedura pobiera 2 parametry - element drzewka ATreeNode oraz element XML: AIXMLNode, do którego zapisujemy. Ponieważ samo wywołanie tej procedury podaje w parametrze funkcję, której rezultatem jest dodanie elementu XML - nie jest koniecznie samo zapisywanie w procedurze. Wywołanie samej siebie następuje w przypadku, gdy element posiada elementy potomne - wtedy dodawany jest odpowiedni element XML jako parametr nowego wywołania procedury. Tym sposobem uzyskujemy strukturę taką jaka jest w TreeView. Zapiszmy przykładową strukturę drzewka zaznaczonego elementu TreeView. Zatem w parametrze początkowo podajemy właśnie zaznaczony element..
var
XNS: IXMLNode;
begin
XNS := XML.DocumentElement; // Przypisanie do XNS elementu głownego - jeśli taki istnieje
Listing(TreeView.Selected, XNS); // Wywołanie
end;
I gotowe :)
Tworzenie interfejsów.
Nasz plik XML posiada swoistą, uporządkowaną budowę - nie jest ona chaotyczna - każdy owoc posiada te same parametry - można więc ułatwić sobie pracę wprowadzając właśnie tą budowę - pewien porządzek. Posłuży nam nasz początkowy plik XML tej postaci:
<?xml version="1.0" encoding="UTF-8"?>
<Owoce>
<Poziomka>
<cena>22.65</cena>
<dostawca>Niemcy</dostawca>
</Poziomka>
<Gruszka>
<cena>12.75</cena>
<dostawca>Polska</dostawca>
</Gruszka>
<Banan>
<cena>12.75</cena>
<dostawca>Portugalia</dostawca>
</Banan>
</Owoce>
a) Wybieramy File - New - Other
b) Z zakładki New wybieramy XML Data Binding
c) W polu "Schema or XML Data File wybieramy nasz plik", a następnie "Next"
d) Pojawi nam się wykaz wszystkich typów danych. Przy każdym możemy nadawać różne parametry, takie jak "Tylko do odczytu".
e) Klikamy "Next" i pojawia nam się gotowy intefejs.
f) Klikamy "Finish" i gotowy kod ląduje u nas w osobnym Unicie projektu.
Wygenerowany kod powinien wyglądać mniej więcej tak:
{*****************************************}
{ }
{ Delphi XML Data Binding }
{ }
{ Generated on: 2004-09-04 10:20:50 }
{ Generated from: C:\test.xml }
{ Settings stored in: C:\test.xdb }
{ }
{*****************************************}
unit Unit2;
interface
uses xmldom, XMLDoc, XMLIntf;
type
{ Forward Decls }
IXMLOwoceType = interface;
IXMLPoziomkaType = interface;
IXMLGruszkaType = interface;
IXMLBananType = interface;
{ IXMLOwoceType }
IXMLOwoceType = interface(IXMLNode)
['{8793158D-4E99-413A-9487-FFFC2D34D4CD}']
{ Property Accessors }
function Get_Poziomka: IXMLPoziomkaType;
function Get_Gruszka: IXMLGruszkaType;
function Get_Banan: IXMLBananType;
{ Methods & Properties }
property Poziomka: IXMLPoziomkaType read Get_Poziomka;
property Gruszka: IXMLGruszkaType read Get_Gruszka;
property Banan: IXMLBananType read Get_Banan;
end;
{ IXMLPoziomkaType }
IXMLPoziomkaType = interface(IXMLNode)
['{0D820CF2-0886-4EA1-BCDC-66D744F87048}']
{ Property Accessors }
function Get_Kolor: WideString;
function Get_Cena: WideString;
function Get_Dostawca: WideString;
procedure Set_Kolor(Value: WideString);
procedure Set_Cena(Value: WideString);
procedure Set_Dostawca(Value: WideString);
{ Methods & Properties }
property Kolor: WideString read Get_Kolor write Set_Kolor;
property Cena: WideString read Get_Cena write Set_Cena;
property Dostawca: WideString read Get_Dostawca write Set_Dostawca;
end;
{ IXMLGruszkaType }
IXMLGruszkaType = interface(IXMLNode)
['{F1AC9DBF-6B3C-4AF4-A171-83630A1ED37C}']
{ Property Accessors }
function Get_Kolor: WideString;
function Get_Cena: WideString;
function Get_Dostawca: WideString;
procedure Set_Kolor(Value: WideString);
procedure Set_Cena(Value: WideString);
procedure Set_Dostawca(Value: WideString);
{ Methods & Properties }
property Kolor: WideString read Get_Kolor write Set_Kolor;
property Cena: WideString read Get_Cena write Set_Cena;
property Dostawca: WideString read Get_Dostawca write Set_Dostawca;
end;
{ IXMLBananType }
IXMLBananType = interface(IXMLNode)
['{DA5BE9D8-9207-4AEB-BA4A-425AC79C9DB4}']
{ Property Accessors }
function Get_Kolor: WideString;
function Get_Cena: WideString;
function Get_Dostawca: WideString;
procedure Set_Kolor(Value: WideString);
procedure Set_Cena(Value: WideString);
procedure Set_Dostawca(Value: WideString);
{ Methods & Properties }
property Kolor: WideString read Get_Kolor write Set_Kolor;
property Cena: WideString read Get_Cena write Set_Cena;
property Dostawca: WideString read Get_Dostawca write Set_Dostawca;
end;
{ Forward Decls }
TXMLOwoceType = class;
TXMLPoziomkaType = class;
TXMLGruszkaType = class;
TXMLBananType = class;
{ TXMLOwoceType }
TXMLOwoceType = class(TXMLNode, IXMLOwoceType)
protected
{ IXMLOwoceType }
function Get_Poziomka: IXMLPoziomkaType;
function Get_Gruszka: IXMLGruszkaType;
function Get_Banan: IXMLBananType;
public
procedure AfterConstruction; override;
end;
{ TXMLPoziomkaType }
TXMLPoziomkaType = class(TXMLNode, IXMLPoziomkaType)
protected
{ IXMLPoziomkaType }
function Get_Kolor: WideString;
function Get_Cena: WideString;
function Get_Dostawca: WideString;
procedure Set_Kolor(Value: WideString);
procedure Set_Cena(Value: WideString);
procedure Set_Dostawca(Value: WideString);
end;
{ TXMLGruszkaType }
TXMLGruszkaType = class(TXMLNode, IXMLGruszkaType)
protected
{ IXMLGruszkaType }
function Get_Kolor: WideString;
function Get_Cena: WideString;
function Get_Dostawca: WideString;
procedure Set_Kolor(Value: WideString);
procedure Set_Cena(Value: WideString);
procedure Set_Dostawca(Value: WideString);
end;
{ TXMLBananType }
TXMLBananType = class(TXMLNode, IXMLBananType)
protected
{ IXMLBananType }
function Get_Kolor: WideString;
function Get_Cena: WideString;
function Get_Dostawca: WideString;
procedure Set_Kolor(Value: WideString);
procedure Set_Cena(Value: WideString);
procedure Set_Dostawca(Value: WideString);
end;
{ Global Functions }
function GetOwoce(Doc: IXMLDocument): IXMLOwoceType;
function LoadOwoce(const FileName: WideString): IXMLOwoceType;
function NewOwoce: IXMLOwoceType;
implementation
{ Global Functions }
function GetOwoce(Doc: IXMLDocument): IXMLOwoceType;
begin
Result := Doc.GetDocBinding('Owoce', TXMLOwoceType) as IXMLOwoceType;
end;
function LoadOwoce(const FileName: WideString): IXMLOwoceType;
begin
Result := LoadXMLDocument(FileName).GetDocBinding('Owoce', TXMLOwoceType) as IXMLOwoceType;
end;
function NewOwoce: IXMLOwoceType;
begin
Result := NewXMLDocument.GetDocBinding('Owoce', TXMLOwoceType) as IXMLOwoceType;
end;
{ TXMLOwoceType }
procedure TXMLOwoceType.AfterConstruction;
begin
RegisterChildNode('Poziomka', TXMLPoziomkaType);
RegisterChildNode('Gruszka', TXMLGruszkaType);
RegisterChildNode('Banan', TXMLBananType);
inherited;
end;
function TXMLOwoceType.Get_Poziomka: IXMLPoziomkaType;
begin
Result := ChildNodes['Poziomka'] as IXMLPoziomkaType;
end;
function TXMLOwoceType.Get_Gruszka: IXMLGruszkaType;
begin
Result := ChildNodes['Gruszka'] as IXMLGruszkaType;
end;
function TXMLOwoceType.Get_Banan: IXMLBananType;
begin
Result := ChildNodes['Banan'] as IXMLBananType;
end;
{ TXMLPoziomkaType }
function TXMLPoziomkaType.Get_Kolor: WideString;
begin
Result := AttributeNodes['kolor'].Text;
end;
procedure TXMLPoziomkaType.Set_Kolor(Value: WideString);
begin
SetAttribute('kolor', Value);
end;
function TXMLPoziomkaType.Get_Cena: WideString;
begin
Result := ChildNodes['cena'].Text;
end;
procedure TXMLPoziomkaType.Set_Cena(Value: WideString);
begin
ChildNodes['cena'].NodeValue := Value;
end;
function TXMLPoziomkaType.Get_Dostawca: WideString;
begin
Result := ChildNodes['dostawca'].Text;
end;
procedure TXMLPoziomkaType.Set_Dostawca(Value: WideString);
begin
ChildNodes['dostawca'].NodeValue := Value;
end;
{ TXMLGruszkaType }
function TXMLGruszkaType.Get_Kolor: WideString;
begin
Result := AttributeNodes['kolor'].Text;
end;
procedure TXMLGruszkaType.Set_Kolor(Value: WideString);
begin
SetAttribute('kolor', Value);
end;
function TXMLGruszkaType.Get_Cena: WideString;
begin
Result := ChildNodes['cena'].Text;
end;
procedure TXMLGruszkaType.Set_Cena(Value: WideString);
begin
ChildNodes['cena'].NodeValue := Value;
end;
function TXMLGruszkaType.Get_Dostawca: WideString;
begin
Result := ChildNodes['dostawca'].Text;
end;
procedure TXMLGruszkaType.Set_Dostawca(Value: WideString);
begin
ChildNodes['dostawca'].NodeValue := Value;
end;
{ TXMLBananType }
function TXMLBananType.Get_Kolor: WideString;
begin
Result := AttributeNodes['kolor'].Text;
end;
procedure TXMLBananType.Set_Kolor(Value: WideString);
begin
SetAttribute('kolor', Value);
end;
function TXMLBananType.Get_Cena: WideString;
begin
Result := ChildNodes['cena'].Text;
end;
procedure TXMLBananType.Set_Cena(Value: WideString);
begin
ChildNodes['cena'].NodeValue := Value;
end;
function TXMLBananType.Get_Dostawca: WideString;
begin
Result := ChildNodes['dostawca'].Text;
end;
procedure TXMLBananType.Set_Dostawca(Value: WideString);
begin
ChildNodes['dostawca'].NodeValue := Value;
end;
end.
Jak widzimy są to nowe interfejsy - naszych już typów, które śmiało można używać w programie. Dopiszcie do sekcji Uses programu: "Unit2" i możemy już zadeklarować własne interfejsy..
var
Owoc: IXMLOwoceType;
Jes to zmienna wskazująca na interfejs "Owoce". Istotnym faktem jest, że nowo stworzone interfejsy są budowane na podstawie struktury naszego XMLa. Każdy element potomny jest traktowany jako właściwość, na przykład w naszym przypadku:
IXMLOwoceType = interface(IXMLNode)
['{8793158D-4E99-413A-9487-FFFC2D34D4CD}']
{ Property Accessors }
function Get_Poziomka: IXMLPoziomkaType;
function Get_Gruszka: IXMLGruszkaType;
function Get_Banan: IXMLBananType;
{ Methods & Properties }
property Poziomka: IXMLPoziomkaType read Get_Poziomka; // Właściwość Poziomka
property Gruszka: IXMLGruszkaType read Get_Gruszka; // Właściwość Gruszka
property Banan: IXMLBananType read Get_Banan; // Właściwość Banan
end;
Nazwa właściwości tej jest taka sama jak nazwa tagu (LocalName). Również atrybuty są tak samo nazwane i potraktowane jak parametry - jednak nie ma ich w naszym przykładzie.
Dodatkowo nasz szablon stworzył globalne funkcje:
function GetOwoce(Doc: IXMLDocument): IXMLOwoceType;
function LoadOwoce(const FileName: WideString): IXMLOwoceType;
function NewOwoce: IXMLOwoceType;
Są one tworzone na podstawie elementu głownego, u nas są to "Owoce".
function GetOwoce(Doc: IXMLDocument): IXMLOwoceType; - ładuje dane z dokumentu XML zdefiniowanego w parametrze
function LoadOwoce(const FileName: WideString): IXMLOwoceType; - ładuje dane z podanego pliku i tworzy nowy egzemplarz TXMLDocument.
function NewOwoce: IXMLOwoceType; - tworzy nowy egzemplarz naszych danych.
Aby zrozumieć mechanizm ten - spójrzmy na taki przykład:
var
Owoc: IXMLOwoceType;
begin
Owoc := GetOwoce(XML);
Memo1.Lines.Add(Owoc.Poziomka.Kolor);
end;
Zadeklarowaliśmy zmienną Owoc typu IXMLOwoceType. Następnie wykorzystaliśmy tu funkcję globalną GetOwoce - parametrem jest u nas "XML" - jest to nasz komponent, w którym przechowywane są dane. Następnie w bardzo prosty sposób wyświetlamy kolor poziomki :)
Podsumowanie
Co tu dużo pisać :] - XML jest doskonałym narzędziem. Na koniec odsyłam jeszcze raz do artykułu Adama Boducha: http://4programmers.net/file.php?id=1239 (a właściwie to kursu) gdzie znajdziecie nieco dokładniejszy opis budowy samych plików i trochę na temat styli CSS. Dużo informacji można też znaleźć na stronie http://www.xml.com oraz oczywiście w Helpie Delphi - gdzie znajduje się szczegółowy opis komponentu TXMLDocument. Pamiętajcie, że to co opisałem to tylko wierzchołek góry lodowej.
a jak w delphi 2005?
Pytanie czy ktoś wie jak utworzyć <owoc> z poziomu kodu???
czy ja poznaję ten tekst z Savage? Bodajże vol. 5?
Dla tych którzy nie mają TXMLDocument POLECAM TjanXMLParser2 (można znaleźć na torry.net) na jego podstawie właśnie zrobiłem taki TXMLIniFile który daje kompatybilność plików INI z plikami XML. Może kiedyś dam na 4p.
zarabiście normalnie bede sobie mogł aktualne kursy walut do programika strzelic a wlaśnie czegos takiego szukałem i nie myslełem że znajde8)
Bardzo dobry arykuł. Gratuluję.
Ale czy ktoś z płatnym delphi może mi podać moduł, w którym znajduje się klasa TXMLDocument?
Pytam dlatego, że pomimo tego, że zakładki "Internet" nie ma w Personalu, to jest w nim moduł Sockets i da się z niego zainstalować komponenty TServer i TClient Socket. Może to samo dało by się zrobić z TXMLDocument?
Naprawdę dobry artykuł. Gratuluję.
Fajnie napisany artykulik. Szkoda tylko, że m.in. zakładka "Internet" wraz z całą gamą komponentów jest tylko w płatnym Delphi, a nie w wersji Personal :/
Wow...ciekawe
Przyda się :)
wszystko jest smieszne i fajne, ale nie latwiej uzyc plikow ini ? nie rozumiem czemu ludzie tak lubia sie w tym babrac skoro do ini zapisujesz sie szybciej i prosciej. Czemu uzywacie XMLa?
Kilka przewag XML nad INI:
minusem może być np. nieco większa objętość, co przy obecnych łączach nie jest wielką wadą
Artykuł czytelny, łatwy w zrozumieniu i nie nudzący jak to czasem bywa.
Good work :)
Gut text, gut text - szkoda, że odkryłem XML w Delphi już przed przeczytaniem tego :)