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.

13 komentarzy

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:

  • hierarchiczność
  • walidacja struktury dzięki DTD
  • możliwość umieszczenia w dowolnym miejscu jednego pliku XML zawartości drugiego poprzez łącze

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 :)