Classe: Utilizando XML como se fosse um arquivo INI

Delphi

31/03/2007

Bom, um tempo atrás eu encontrei um exemplo de código na net que mostrava como criar uma classe para utilizar um arquivo XML como um arquivo INI, ontem, resolvi mexer nele por necessidade e me deparei com algo que podia ser melhorado um pouco...

* [b:b927c0f285]Antes:[/b:b927c0f285] era bem básico, arquivo XML funcionava como [b:b927c0f285]1 único[/b:b927c0f285] arquivo INI, com funcoes de leitura e escrita de valores;
* [b:b927c0f285]Agora:[/b:b927c0f285] arquivo XML funciona com [b:b927c0f285]vários[/b:b927c0f285] arquivos INI aninhados em seu interior, adicionado um procedimento para importar novos arquivos INI para o corpo do XML. Adicionada funcoes/procedimentos de exclusão de INI, verificação e exclusão de sections e keys além de contadores diversos;

Um exemplo de utilização:
 var  ini: TINIXML;
 begin
  memo1.Clear;
  ini := TINIXML.Create(´meuINI.XML´);
  ini.INIfile := ´Config´; //seta o INI interno ou cria um novo caso nao haja
  ini.Backup := False; //realiza ou não backup do antigo arquivo
  ini.ValuesAsNode := True; //chaves como nós ou como parâmetros
  ini.WriteString(´section1´,´key1´,´valor´);
  Edit1.Text := ini.ReadString(´section1´,´key1´,´default´);
  ini.Free;
 end;


Um exemplo de importação:
var  ini: TINIXML;
begin
 ini := TINIXML.Create(´meuarquivo.XML´); //cria ou utiliza um existente
 ini.KeyAsNode := True;
 ini.ImportINI(´meu arquivo.INI´,´MeuINI´); // a importação seta o INI atual
 ini.ReadSections(memo1.Lines);
 ini.Free;
end;


Bom se alguém se interessar e quiser adicionar algo ou fazer alguma melhoria, segue a classe:

unit uINIXml;

interface

uses
Forms, SysUtils, Windows, Classes, XmlIntf, XMLDoc, INIFiles;

type
  TINIXML = class
  private
   FModified: Boolean;
   FFileName: string;
   FXMLDoc: TXMLDocument;
   FBackup: Boolean;
   FINIfile:string;
   FAsNode: Boolean;
   function GetVersion: string;
   function GetINIfile: string;
   function Up(const s:string):string;
   procedure SetINIfile(const Value: string);
   procedure CheckINIfile;
  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);
   procedure SaveNow;

   //file
   function XMLfile:string;

   //delete
   function EraseSection(const Section: string):boolean;
   function DeleteKey(const Section,Key:string):boolean;
   function EraseINI:boolean;

   //verif
   function SectionExists(const Section: string):boolean;
   function KeyExists(const Section, Key: string):boolean;
   procedure ReadINIfiles(Strings:TStrings);
   procedure ReadSections(Strings:TStrings);
   procedure ReadKeys(const Section:string; Strings:TStrings);

   //counters
   function countINIfiles:integer;
   function countSections(const INIfile:string):integer; overload;
   function countSections:integer; overload;
   function countKeys(const INIfile,Section:string):integer; overload;
   function countKeys(Section:string):integer; overload;

   //import
   function ImportINI(const fileName, InternalINIfile: string):boolean;

   property Backup: Boolean read FBackup write FBackup;
   property Version: string read GetVersion;
   property INIfile: string read GetINIfile write SetINIfile;
   property KeyAsNode: Boolean read FAsNode write FAsNode default False;
  end;

implementation

{ TINIXML }

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

constructor TINIXML.Create;
var fxml:string;
begin
 fxml := ExtractFileExt(Application.Exename);
 fxml := Copy(Application.Exename,1,length(Application.Exename)-length(fxml));
 Create(fxml+´.xml´);
end;

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

function TINIXML.XMLfile: string;
begin
  Result := FFileName;
end;

procedure TINIXML.CheckINIfile;
begin
 if (not (Trim(FINIfile) <> ´´)) then raise Exception.Create(´Error: Empty Internal INI file!´);
end;

function TINIXML.EraseSection(const Section: string):boolean;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 Result := False;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile);
 if Assigned(Node.ChildNodes.FindNode(Up(Section))) then
    Result := Node.ChildNodes.Delete(Up(Section)) >= 0;
 Node := nil;
 if Result then
   FModified := True;
end;

function TINIXML.DeleteKey(const Section, Key: string):boolean;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 Result := False;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));;
 if Assigned(Node) then
   begin
     if (Node.HasChildNodes) and (Node.ChildNodes.IndexOf(Up(Key))>=0) then
        Result := Node.ChildNodes.Delete(Up(Section)) >= 0
     else
        Result := Node.AttributeNodes.Delete(Up(key)) >= 0;
   end;
 Node := nil;
 if Result then
   FModified := True;
end;

function TINIXML.EraseINI: boolean;
begin
 CheckINIfile;
 Result := False;
 if Assigned(FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile)) then
    Result := FXMLDoc.DocumentElement.ChildNodes.Delete(FINIfile) >= 0;
 if Result then
  begin
   FINIfile := EmptyStr;
   FModified := True;
  end;
end;

function TINIXML.GetINIfile: string;
begin
  Result := FINIfile;
end;

function TINIXML.GetVersion: string;
begin
 Result := ´1.00´;
end;

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

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

function TINIXML.ReadString(const Section, Key, default: string): string;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));
 if Assigned(Node) then
   begin
     if Node.HasAttribute(Up(Key)) then
       Result := Node.Attributes[Up(Key)]
     else
       if (Node.HasChildNodes) and (Node.ChildNodes.IndexOf(Up(Key))>=0) then
         Result := Node.ChildNodes.Nodes[Node.ChildNodes.IndexOf(Up(Key))].NodeValue
       else
         Result := default;
   end
 else
   Result := default;
 Node := nil
end;

procedure TINIXML.Save;
begin
 if not FModified then
  Exit;
 if FBackup then
  CopyFile(PChar(FFileName), PChar(FFileName + ´.bak´), False);
 FXMLDoc.SaveToFile(FFileName);
 FModified := False;
end;

procedure TINIXML.SetINIfile(const Value: string);
begin
 FINIfile := Up(Value);
 if not Assigned(FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile)) then
  begin
    if not FXMLDoc.Active then FXMLDoc.Active := True;
    FXMLDoc.DocumentElement.AddChild(FINIfile);
  end;
end;

function TINIXML.Up(const s: string): string;
begin
  Result := StringReplace(AnsiUpperCase(s),´ ´,´´,[rfReplaceAll])
end;

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

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

procedure TINIXML.WriteString(const Section, Key, Value: string);
var
 Node: IXMLNode;
begin
 if ReadString(Up(Section), Up(Key), ´´) = Value then
  Exit;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));
 if not Assigned(Node) then
  Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).AddChild(Up(Section));
 if ((Node.HasAttribute(Up(Key))) or (not FAsNode)) and (not Assigned(Node.ChildNodes.FindNode(Up(Key)))) then
    Node.Attributes[Up(Key)] := Value
 else
  begin
    if not Assigned(Node.ChildNodes.FindNode(Up(Key))) then
      Node.AddChild(Up(Key)).Text := Value
    else
      Node.ChildNodes.FindNode(Up(Key)).Text := Value;
  end;
 FModified := True;
 Node := nil
end;

procedure TINIXML.SaveNow;
begin
 FModified := True;
 Save;
end;

function TINIXML.SectionExists(const Section: string): boolean;
begin
 CheckINIfile;
 Result := Assigned(FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section)));
end;

function TINIXML.KeyExists(const Section, Key: string): boolean;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 Result := False;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));
 if Assigned(Node) then
   begin
    Result := Node.HasAttribute(Up(Key));
    if (not Result) and (Node.HasChildNodes) then
       Result := Assigned(Node.ChildNodes.FindNode(Up(Key)));
   end;
 Node := nil;
end;

procedure TINIXML.ReadINIfiles(Strings: TStrings);
var x:integer;
begin
  Strings.Clear;
  for x:=0 to pred(FXMLDoc.DocumentElement.ChildNodes.Count) do
    Strings.Add(FXMLDoc.DocumentElement.ChildNodes[x].NodeName);
end;

procedure TINIXML.ReadSections(Strings: TStrings);
var
 Node: IXMLNode;
 x:longint;
begin
 CheckINIfile;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile);
 Strings.Clear;
 if Assigned(Node) then
   for x:=0 to pred(Node.ChildNodes.Count) do
    begin
     if Node.ChildNodes[x].NodeName[1] <> ´´ then
        Strings.Add(Node.ChildNodes[x].NodeName);
    end;
 Node := nil;
end;

procedure TINIXML.ReadKeys(const Section: string; Strings: TStrings);
var
 Node: IXMLNode;
 x:longint;
begin
 CheckINIfile;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));
 Strings.Clear;
 if Assigned(Node) then
   begin
     for x:=0 to pred(Node.AttributeNodes.Count) do
       Strings.Add(Node.AttributeNodes[x].NodeName);
     for x:=0 to pred(Node.ChildNodes.Count) do
       if Node.ChildNodes[x].NodeName[1] <> ´´ then
          Strings.Add(Node.ChildNodes[x].NodeName);
   end;
 Node := nil;
end;

function TINIXML.ImportINI(const fileName, InternalINIfile: string): boolean;
var
  //ini
  lINIFile: TIniFile;
  lINIname: string;
  lSections, lItems: TStringList;
  iSections, iItems: integer;
  //xml
  pXML,lNode: IXMLNode;

begin
  Result := False;
  lINIname := Up(InternalINIfile);
  if Trim(lINIname) = EmptyStr then
   raise Exception.Create(´Error: Empty INIname informed!´);
  if not Assigned(FXMLDoc.DocumentElement.ChildNodes.FindNode(lINIname)) then
   begin
    if not FXMLDoc.Active then FXMLDoc.Active := True;
    pXML := FXMLDoc.DocumentElement.AddChild(lINIname);
   end
  else
   raise Exception.Create(´Error: Internal INIname already exists!´);

  lINIFile := TIniFile.Create(FileName); try
  lSections := TStringList.Create; try
  lItems := TStringList.Create; try

  lINIFile.ReadSections(lSections);

  for iSections := 0 to pred(lSections.Count) do
  begin
    lItems.Clear;
    lINIFile.ReadSection(lSections[iSections],lItems);
    lNode := pXML.AddChild(StringReplace(lSections[iSections],´ ´,´´,[rfReplaceAll]));
    for iItems := 0 to pred(lItems.Count) do
    begin
      if not FAsNode then
        lNode.Attributes[lItems[iItems]] :=
          lINIFile.ReadString(lSections[iSections],lItems[iItems],´´)
      else
        lNode.AddChild(lItems[iItems]).Text :=
          lINIFile.ReadString(lSections[iSections],lItems[iItems],´´);
    end;
    lNode := nil;
    pXML  := nil;
    FINIfile := lINIname;
    FModified := True;
    Save;
    Result := True;
  end;

  finally lItems.Free; end;
  finally lSections.Free; end;
  finally lINIFile.Free; end;
end;

//COUNTERS
function TINIXML.countINIfiles: integer;
begin
  Result := FXMLDoc.DocumentElement.ChildNodes.Count;
end;

function TINIXML.countSections(const INIfile:string): integer;
var
 Node: IXMLNode;
begin
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(INIfile);
 if Assigned(Node) then
   Result := Node.ChildNodes.Count
 else
   Result := 0;
 Node := nil;
end;

function TINIXML.countSections: integer;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile);
 if Assigned(Node) then
   Result := Node.ChildNodes.Count
 else
   Result := 0;
 Node := nil;
end;

function TINIXML.countKeys(const INIfile, Section: string): integer;
var
 Node: IXMLNode;
 auxi: integer;
begin
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(INIfile).ChildNodes.FindNode(Up(Section));
 if Assigned(Node) then
   begin
     Result := Node.AttributeNodes.Count;
     if Result = 0 then
       for auxi := 0 to pred(Node.ChildNodes.Count) do
          if Node.ChildNodes[auxi].NodeName[1] <> ´´ then
             inc(Result);
   end
 else
   Result := 0;
 Node := nil;
end;

function TINIXML.countKeys(Section: string): integer;
var
 Node: IXMLNode;
 auxi:integer;
begin
 CheckINIfile;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));
 if Assigned(Node) then
   begin
     Result := Node.AttributeNodes.Count;
     if Result = 0 then
       for auxi := 0 to pred(Node.ChildNodes.Count) do
          if Node.ChildNodes[auxi].NodeName[1] <> ´´ then
             inc(Result);
   end
 else
   Result := 0;
 Node := nil;
end;

end.



Richard Lima

Richard Lima

Curtidas 0

Respostas

Richard Lima

Richard Lima

31/03/2007

[b:a1c479efea]>>> Movendo nodes XML <<<[/b:a1c479efea]

Depois de estar utilizando um XML com a facilidade de um arquivo INI, tive a necessidade de reposicionar os INI internos, para isso, criei uma funcao de mover os nós do XML para cima ou para baixo, estendendo a funcionalidade para as sections e keys...

1 - criei um [b:a1c479efea]type[/b:a1c479efea] para a direção do movimento
type TINIXmlMoveDir = (moveDown,moveUp);


2 - criei uma funcao na área private da classe
function  MoveNode(const Node: IXMLNode; moveDirection:TINIXmlMoveDir):boolean;


- implementação:
function TINIXML.MoveNode(const Node: IXMLNode; moveDirection:TINIXmlMoveDir):boolean;
begin
 Result := False;
 case moveDirection of
  moveUp: if Node.PreviousSibling <> nil then
             Result := Node.ParentNode.DOMNode.insertBefore(Node.DOMNode,Node.PreviousSibling.DOMNode) <> nil;
  moveDown: if Node.NextSibling <> nil then
               if Node.NextSibling.NextSibling <> nil then
                 Result := Node.ParentNode.DOMNode.insertBefore(Node.DOMNode,Node.NextSibling.NextSibling.DOMNode) <> nil
               else
                 Result := Node.ParentNode.DOMNode.insertBefore(Node.NextSibling.DOMNode,Node.DOMNode) <> nil;
 end;
end;


3 - adicionei 3 funcões na área public da classe
   //move
   function MoveINI(const INIfile:string; moveDirection:TINIXmlMoveDir):boolean;
   function MoveSection(const Section:string; moveDirection:TINIXmlMoveDir):boolean;
   function MoveKey(const Section, Key: string; moveDirection:TINIXmlMoveDir):boolean;


- implementação:
function TINIXML.MoveINI(const INIfile: string;
  moveDirection: TINIXmlMoveDir):boolean;
var
 Node: IXMLNode;
begin
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(Up(INIfile));
 if Assigned(Node) then
    Result := MoveNode(Node,moveDirection);
 Node := nil;
 if Result then FModified := Result;
end;

function TINIXML.MoveSection(const Section:string; moveDirection:TINIXmlMoveDir):boolean;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section));
 if Assigned(Node) then
    Result := MoveNode(Node,moveDirection);
 Node := nil;
 if Result then FModified := Result;
end;

function TINIXML.MoveKey(const Section, Key: string; moveDirection:TINIXmlMoveDir): boolean;
var
 Node: IXMLNode;
begin
 CheckINIfile;
 //only node keys
 Node := FXMLDoc.DocumentElement.ChildNodes.FindNode(FINIfile).ChildNodes.FindNode(Up(Section)).ChildNodes.FindNode(Up(Key));
 if Assigned(Node) then
    Result := MoveNode(Node,moveDirection);
 Node := nil;
 if Result then FModified := Result;
end;


Exemplo:
[b:a1c479efea]XML:[/b:a1c479efea]
<Configuration>
<CONFIGURACAO>
<SECTIONX>
<KEY1>valor</KEY1>
<KEY2>valor</KEY2>
</SECTIONX>
</CONFIGURACAO>
</Configuration>


[b:a1c479efea]Código:[/b:a1c479efea]
 var  ini: TINIXML;
begin
  ini := TINIXML.Create(´c:\meuINI.XML´);
  ini.INIfile := ´Configuracao´;
  Ini.MoveKey(´sectionx´,´key2´,moveUp);
  ini.Free;
end;


[b:a1c479efea]Resultado XML:[/b:a1c479efea]
<Configuration>
<CONFIGURACAO>
<SECTIONX>
<KEY2>valor</KEY2>
<KEY1>valor</KEY1>
</SECTIONX>
</CONFIGURACAO>
</Configuration>


até!


GOSTEI 0
POSTAR