tags:

views:

171

answers:

6

hi all

can anyone help me to learn how to use XML. As a test i want to use XML instead of INI files for saving a program settings.

Thanks

A: 

Depending on which edition of Delphi you are using, you may or may not have the TXMLDocument component available. If you do not, then you will have to use a third-party XML library/component. I prefer libxml2 myself, and it has a binding available for Pascal/Delphi.

Remy Lebeau - TeamB
+7  A: 

Rigo, you can use the IXMLDocument interface or the TXMLDocument object to interact with a XML document.

You can check these links for more information about XML and Delphi

Check this sample code , to learn the basics about xml management using the IXMLDocument interface.

program Delphi_XmlSaveSettings;

{$APPTYPE CONSOLE}
uses
  ActiveX,
  SysUtils,
  XmlDoc,
  XmlIntf;
//this class mimic the basics functionalities of an TIniFile
//you can improve a lot the code adding exception handling and more methods for specifics tasks.
type
  TXMLSettings = class 
  private
    FFileName: string;
    FXMLDoc: IXMLDocument; //Main XMLObject
  public
    constructor Create(const FileName: string); overload;
    destructor  Destroy; override;
    function    ReadString(const Section, Key, default: string): string;
    procedure   WriteString(const Section, Key, Value: string);
    function    ReadInteger(const Section, Key: string; default: Integer): Integer;
    procedure   WriteInteger(const Section, Key: string; Value: Integer);
    function    ReadBoolean(const Section, Key: string; default: Boolean): Boolean;
    procedure   WriteBoolean(const Section, Key: string; Value: Boolean);
    function    ReadDouble(const Section, Key: string; default: Double): Double;
    procedure   WriteDouble(const Section, Key: string; Value: Double);
    function    ReadDateTime(const Section, Key: string; default: TDatetime): TDateTime;
    procedure   WriteDatetime(const Section, Key: string; Value: TDatetime);
    function    ReadDate(const Section, Key: string; default: TDatetime): TDateTime;
    procedure   WriteDate(const Section, Key: string; Value: TDatetime);
    procedure   Save;
  end;



constructor TXMLSettings.Create(const FileName: string);
begin
  inherited Create;
  FFileName       := FileName;
  FXMLDoc         := NewXMLDocument; //Create  aNew instance of a XML Document
  FXMLDoc.Encoding:= 'UTF-8'; //Set the encoding
  FXMLDoc.Options := [doNodeAutoIndent];//optional, is used to indent the Xml document

  if FileExists(FFileName) then
    FXMLDoc.LoadFromFile(FFileName)
  else
    FXMLDoc.AddChild('Root'); //Create the root Node
end;


destructor TXMLSettings.Destroy;
begin
  Save;
  inherited;
end;

function TXMLSettings.ReadBoolean(const Section, Key: string; default: Boolean): Boolean;
begin
  Result := Boolean(ReadInteger(Section, Key, Integer(default)));
end;

function TXMLSettings.ReadDate(const Section, Key: string; default: TDatetime): TDateTime;
begin
  Result := StrToDate(ReadString(Section, Key, DateToStr(default)));
end;

function TXMLSettings.ReadDateTime(const Section, Key: string; default: TDatetime): TDateTime;
begin
  Result := StrToDateTime(ReadString(Section, Key, DateTimeToStr(default)));
end;

function TXMLSettings.ReadDouble(const Section, Key: string;  default: Double): Double;
begin
  Result := StrToFloat(ReadString(Section, Key, FloatToStr(default)));
end;

function TXMLSettings.ReadInteger(const Section, Key: string; default: Integer): Integer;
begin
  Result := StrToInt(ReadString(Section, Key, IntToStr(default)));
end;

function TXMLSettings.ReadString(const Section, Key, default: string): string; 
var
  XMLNode: IXMLNode;
begin
  XMLNode := FXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
  if Assigned(XMLNode) and XMLNode.HasAttribute(Key) then //Check if exist the Key
    Result := XMLNode.Attributes[Key]
  else
    Result := default;
end;

procedure TXMLSettings.Save;
begin
  FXMLDoc.SaveToFile(FFileName);
end;

procedure TXMLSettings.WriteBoolean(const Section, Key: string; Value: Boolean);
begin
  WriteInteger(Section, Key, Integer(Value));
end;

procedure TXMLSettings.WriteDate(const Section, Key: string; Value: TDatetime);
begin
  WriteString(Section, Key, DateToStr(Value));
end;

procedure TXMLSettings.WriteDatetime(const Section, Key: string;  Value: TDatetime);
begin
  WriteString(Section, Key, DateTimeToStr(Value));
end;

procedure TXMLSettings.WriteDouble(const Section, Key: string; Value: Double);
begin
  WriteString(Section, Key, FloatToStr(Value));
end;

procedure TXMLSettings.WriteInteger(const Section, Key: string; Value: Integer);
begin
  WriteString(Section, Key, IntToStr(Value));
end;

procedure TXMLSettings.WriteString(const Section, Key, Value: string);
var
  XMLNode: IXMLNode;
begin
  XMLNode := FXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
  if not Assigned(XMLNode) then
  XMLNode := FXMLDoc.DocumentElement.AddChild(Section);
  XMLNode.Attributes[Key] := Value;
end;


Procedure SaveSettings; //Store the settings
Var
  AppSettings :  TXMLSettings;
begin
  AppSettings:=TXMLSettings.Create(ExtractFilePath(ParamStr(0))+'MySettings.xml');
  try
   AppSettings.WriteString('Server','Type','SQLServer');
   AppSettings.WriteString('Server','User','root');
   AppSettings.WriteInteger('Server','port',1433);
   AppSettings.WriteString('Server','IP','192.168.1.1');
   AppSettings.WriteString('Server','Database','Prod');
   AppSettings.WriteBoolean('Server','WindowsAuth',False);
   AppSettings.WriteDouble('Server','Latency',25.90892);
   AppSettings.WriteDatetime('Server','LastAccess',Now);
   AppSettings.WriteDate('Server','ActualDate',Now);
   AppSettings.Save;
  finally
  AppSettings.Free;
  end;
  Writeln('Settings Saved');
end;


Procedure ShowSettings;//Read the settings
Var
  AppSettings :  TXMLSettings;
begin
  AppSettings:=TXMLSettings.Create(ExtractFilePath(ParamStr(0))+'MySettings.xml');
  try
   Writeln(Format('Type         %s',[AppSettings.ReadString('Server','Type','')]));
   Writeln(Format('Port         %d',[AppSettings.ReadInteger('Server','port',0)]));
   Writeln(Format('IP           %s',[AppSettings.ReadString('Server','IP','')]));
   Writeln(Format('Database     %s',[AppSettings.ReadString('Server','Database','')]));
   Writeln(Format('WindowsAuth  %s',[BoolToStr(AppSettings.ReadBoolean('Server','WindowsAuth',True),True)]));
   Writeln(Format('Latency      %g',[AppSettings.ReadDouble('Server','Latency',0)]));
   Writeln(Format('LastAccess   %s',[DateTimeToStr(AppSettings.ReadDateTime('Server','LastAccess',Now-1))]));
   Writeln(Format('ActualDate   %s',[DateToStr(AppSettings.ReadDate('Server','ActualDate',Now-1))]));
  finally
  AppSettings.Free;
  end;
end;

begin
  try
    CoInitialize(nil); //only necesary in console applications
    try
      SaveSettings; //Save the sample settings
      ShowSettings; //Read the stored settings
      Readln;
    finally
    CoUninitialize; //only necesary in console applications
    end;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
RRUZ
It is a good example, but just a note. You mimic the INI file here. I think that the power of XML over the INI is in the XML flexibility and the endless depth you can have. Good example otherwise :)
Runner
@Runner, i give this sample just as a simple aproach to use the XML to save the settings of an application, obviously the OP can implement a more flexible and complex solution when he knows XML in depth.
RRUZ
+2  A: 

I think RRUZ mostly got it all.

You can also look at SimpleStorage because it was made just with task like this in mind. I use it for all my program settings manipulation. For example here is one of such examples I use (you can see it simplifies code because you don't need to check if a node or a value exist):

The XML:

<Settings>
  <Service>
    <Name>*********</Name>
    <DisplayName>*********</DisplayName>
    <Description>************</Description>
  </Service>
  <Http>
    <ListenQueue>1000</ListenQueue>
    <Port>8778</Port>
  </Http>  
  <IPC>
    <Name>*******</Name>
  </IPC>
  <Modules>
    <Default>
      <Protocol>ISAPI</Protocol>
      <MaxProcNum>5</MaxProcNum>
      <MemoryLimit>500</MemoryLimit>
    </Default>
  </Modules>
</Settings>

And The code:

procedure THydraSettings.LoadSettings(const RootPath: string);
var
  Settings: ISimpleStorage;
  DefaulNode: IElement;
  DefExtDir: string;
begin
  DefExtDir := ExtractFilePath(ParamStr(0)) + DIR_EXTENSIONS;

  Settings := StorageFromFile(RootPath + XML_SettingsFile);
  FExtensionsDir := Settings.Get(cNode_Extensions).AsStringDef(DefExtDir);
  FExtensionsDir := IncludeTrailingPathDelimiter(FExtensionsDir);

  // IPC server based settings
  FIPC.Name := Settings.Get(cNode_IPCName).AsStringDef('AI_Hydra');

  // http server based settings
  FHTTP.ServerPort := Settings.Get(cNode_HttpPort).AsIntegerDef(80);
  FHTTP.ListenQueue := Settings.Get(cNode_ListenQueue).AsIntegerDef(100);

  // get the defaults settings for modules
  DefaulNode := Settings.Get([cNode_Modules, cNode_Default]);
  FProtocol := DefaulNode.Get(cNode_Protocol).AsStringDef('ISAPI');
  FMaxProcNum := DefaulNode.Get(cNode_MaxProcNum).AsIntegerDef(5);
  FMemoryLimit := DefaulNode.Get(cNode_MemoryLimit).AsIntegerDef;
  FTimeout := DefaulNode.Get(cNode_MemoryLimit).AsIntegerDef(30);
  FUnload := DefaulNode.Get(cNode_Unload).AsBooleanDef;
end;
Runner
+1  A: 

In recent versions of Delphi, 2009+ I believe, there is a unit called XMLIniFile which contains an xml wrapper around TIniFile called TXMLIniFile, and also TXMLMemIniFile.

However in my installation of Delphi 2010 XMLIniFile.pas is missing and only XMLIniFile.dcu is present, you can still use it of course though. In addition it seems to be undocumented, even on the Delphi DocWiki.

Alan Clark
A: 

From swissdelphicenter

http://www.swissdelphicenter.ch/torry/showcode.php?id=2269

{This code shows how to use TXMLDocument to save and restore configuration
settings in a XML document. The public methods works the same as a TIniFile.
There is not mutch comment in the code because it is self explaining
and small. Hope this benefit other persons. It is only tested in D7 pro.}

unit uCiaXml;

interface

uses
  Forms, SysUtils, Windows, XmlIntf, XMLDoc;

type
  TXMLConfig = class
  private
    FModified: Boolean;
    FFileName: string;
    FXMLDoc: TXMLDocument;
    FBackup: Boolean;
    function GetVersion: string;
  public
    constructor Create(const FileName: string); overload;
    constructor Create; overload;
    destructor Destroy; override;
    procedure Save;
    function ReadString(const Section, Key, default: string): string;
    procedure WriteString(const Section, Key, Value: string);
    function ReadInteger(const Section, Key: string; default: Integer): Integer;
    procedure WriteInteger(const Section, Key: string; Value: Integer);
    function ReadBoolean(const Section, Key: string; default: Boolean): Boolean;
    procedure WriteBoolean(const Section, Key: string; Value: Boolean);
    property Backup: Boolean read FBackup write FBackup;
    property Version: string read GetVersion;
  end;

implementation

{ TXMLConfig }

constructor TXMLConfig.Create(const FileName: string);
begin
  inherited Create;
  FBackup         := True;
  FFileName       := FileName;
  FXMLDoc         := TXMLDocument.Create(Application);
  FXMLDoc.Options := [doNodeAutoIndent];
  if FileExists(FFileName) then
    FXMLDoc.LoadFromFile(FFileName)
  else 
  begin
    FXMLDoc.Active := True;
    FXMLDoc.AddChild('Configuration');
  end;
end;

constructor TXMLConfig.Create;
begin
  Create(ChangeFileExt(Application.Exename, '_cfg.xml'));
end;

destructor TXMLConfig.Destroy;
begin
  Save;
  FXMLDoc.Destroy;
  inherited;
end;

function TXMLConfig.GetVersion: string;
begin
  Result := '1.00';
end;

function TXMLConfig.ReadBoolean(const Section, Key: string; default: Boolean): Boolean;
begin
  Result := Boolean(ReadInteger(Section, Key, Integer(default)));
end;

function TXMLConfig.ReadInteger(const Section, Key: string; default: Integer): Integer;
begin
  Result := StrToInt(ReadString(Section, Key, IntToStr(default)));
end;

function TXMLConfig.ReadString(const Section, Key, default: string): string;
var
  Node: IXMLNode;
begin
  Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
  if Assigned(Node) and Node.HasAttribute(Key) then
    Result := Node.Attributes[Key]
  else
    Result := default;
end;

procedure TXMLConfig.Save;
begin
  if not FModified then
    Exit;
  if FBackup then

    CopyFile(PChar(FFileName), PChar(FFileName + '.bak'), False);
  FXMLDoc.SaveToFile(FFileName);
  FModified := False;
end;

procedure TXMLConfig.WriteBoolean(const Section, Key: string; Value: Boolean);
begin
  WriteInteger(Section, Key, Integer(Value));
end;

procedure TXMLConfig.WriteInteger(const Section, Key: string; Value: Integer);
begin
  WriteString(Section, Key, IntToStr(Value));
end;

procedure TXMLConfig.WriteString(const Section, Key, Value: string);
var
  Node: IXMLNode;
begin
  if ReadString(Section, Key, '') = Value then
    Exit;
  Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
  if not Assigned(Node) then
    Node := FXMLDoc.DocumentElement.AddChild(Section);
  Node.Attributes[Key] := Value;
  FModified := True;
end;

end.
GX
A: 

To store configuration in a XML file, you can use the OmniXML library. It's pretty simple to use with TPersistent derivates:

lets say:

type
  TConfig = class(TPersistent)
  private
    FSomeOption: string;
  published
    SomeOption: string read FSomeOption write FSomeOption;
  end;

then, with a single line of code you can store it to a xml file:

begin
  TOmniXMLWriter.SaveToFile(Config, ConfigFileName, pfNodes, ofIndent)
end;

where Config is a instance of TConfig.

You can read a bit more about this in my blog (translated from Spanish with Google)

Best regards.

jachguate