XML配置文件读取类[DELPHI]

癫狂编程
2020-08-26

发现用INI做配置的话,实在有太多的东西难以描述,所以自己做了一个XML的配置文件存取类。

需要的同学可以直接拿去用,但希望尊重劳动成果,保留版权信息。

废话不多说,上代码!


unit XMLConfig;

{----------------------------------------------------------------------------}

{ 这个单元用来处理XML配置文件,对配置文件格式有默认要求                             }

{ 格式为,只允许有一个root,然后root下对应配置文件,                               }

{ 所有配置,均使用xml属性存取配置,属性中必须存在Name属性,                         }

{ 不得单独使用下级Node                                                         }

{ PS: 使用NativeXML库作为XML取数基本集,NativeXML请自行获取                      }

{ By Raymond.Zhang @ 2012.07.12 Mail: Acni.ray@gmail.com                     }

{ Tebs Work Group                                                            }

{----------------------------------------------------------------------------}

interface

uses

  NativeXml, System.Classes, System.SysUtils, CommLib,

  System.Generics.Collections;


type


  //为了自动释放的特性,使用接口

  {$REGION 'Interface'}

  IConfigNode = interface

    ['{67323F7D-9E6C-420B-BF1C-92457D829380}']

    function EnmuConfigNames: TStringList;

    function EnmuConfigValues: TStringList;

    function GetName: string;

    function GetValueByConfig(AConfig: string): string;

    function ValueWithDefault(AConfig: string; ADefualt: string):string;

    procedure DeleteConfig(const AConfig: string);

    procedure SetValueByConfig(AConfig: string; const Value: string);

    property Value[AConfig: string]: string read GetValueByConfig write SetValueByConfig; default;

    property Name: string read GetName;

  end;


  IConfigNodes = interface

    ['{56DBB6F5-BD64-4F07-A949-300877B1B787}']

    function AddConfigNode(AName: string): IConfigNode;

    function EnmuConfigNodes: TStringList;

    function GetConfigNodeByIndex(AIndex: Integer): IConfigNode;

    function GetConfigNodeByName(AName: string): IConfigNode;

    function GetConfigNodeCount: Integer;

    procedure DeleteConfig(AName: string);

    property ConfigNode[AName: string]: IConfigNode read GetConfigNodeByName; default;

    property Count: Integer read GetConfigNodeCount;

    property Nodes[AIndex: Integer]: IConfigNode read GetConfigNodeByIndex;

  end;


  IRootNode = interface

    ['{65213F85-0804-4FE1-A726-CFC0F082AC93}']

    function GetConfigsByType(AType: string): IConfigNodes;

    property Configs[AType: string]: IConfigNodes read GetConfigsByType; default;

  end;

  {$ENDREGION}


  TConfigNode = class(TInterfacedObject, IConfigNode)

  private

    FXMLNode: TXmlNode;

    function GetName: string;

  protected

    function GetValueByConfig(AConfig: string): string;

    procedure SetValueByConfig(AConfig: string; const Value: string);

  public

    constructor Create(AXmlNode: TXmlNode);

    destructor Destroy; override;

    function EnmuConfigNames: TStringList;

    function EnmuConfigValues: TStringList;

    function ValueWithDefault(AConfig: string; ADefualt: string):string;

    procedure DeleteConfig(const AConfig: string);

    property Value[AConfig: string]: string read GetValueByConfig write SetValueByConfig; default;

    property Name: string read GetName;

  end;


  TConfigNodes = class(TInterfacedObject, IConfigNodes)

  private

    FType: string;

    FRootNode: TXmlNode;

    FXmlNodes: TList<TXmlNode>;

  protected

    function GetConfigNodeByIndex(AIndex: Integer): IConfigNode;

    function GetConfigNodeByName(AName: string): IConfigNode;

    function GetConfigNodeCount: Integer;

  public

    constructor Create(const ARootNode: TXmlNode; const AType: string);

    destructor Destroy; override;

    function AddConfigNode(AName: string): IConfigNode;

    function EnmuConfigNodes: TStringList;

    procedure DeleteConfig(AName: string);

    property ConfigNode[AName: string]: IConfigNode read GetConfigNodeByName; default;

    property Count: Integer read GetConfigNodeCount;

    property Nodes[AIndex: Integer]: IConfigNode read GetConfigNodeByIndex;

  end;


  TRootNode = class(TInterfacedObject, IRootNode)

  private

    FRootNode: TXmlNode;

  public

    constructor Create(AXmlNode: TXmlNode);

    destructor Destroy; override;

    function GetConfigsByType(AType: string): IConfigNodes;

  end;


  TXMLConfig = class(TObject)

  private

    FAutoSave: Boolean;

    FConfig: TNativeXml;

    FConfigName: string;

    FConfigPath: string;

  protected

    function GetRoot:IRootNode;

  public

    class function RegisterFileInfo(AFileInfo: IFileInfo): Boolean;

    constructor Create(ConfigName: string);

    destructor Destroy; override;

    procedure Save;

    property Root: IRootNode read GetRoot;

    property AutoSave: Boolean read FAutoSave write FAutoSave;

  end;


implementation

var

  AppFileInfo: IFileInfo = nil;

const

  ConfigExt: string = '.config';

  UnRegFileInfo: string = '文件接口未注册,无法获取配置文件路径!';


{ TXMLConfig }


constructor TXMLConfig.Create(ConfigName: string);

begin

  if Assigned(AppFileInfo) then

  begin

    inherited Create;

    FConfigName := ConfigName;

    FConfigPath := AppFileInfo.ConfigPath + ConfigName + ConfigExt;

    FConfig := TNativeXml.Create(nil);

    FConfig.Charset := 'utf-8';

    FConfig.XmlFormat := xfReadable;

    FAutoSave := True;

    if FileExists(FConfigPath) then

      FConfig.LoadFromFile(FConfigPath)

    else begin

      FConfig.VersionString := '1.0';

      FConfig.Root.Name := 'ConfigData';

      Save;

    end;

  end else

    raise ERayException.Create(UnRegFileInfo);

end;


destructor TXMLConfig.Destroy;

begin

  if FAutoSave then Save;

  FreeAndNil(FConfig);

  inherited;

end;


function TXMLConfig.GetRoot: IRootNode;

begin

  Result := TRootNode.Create(FConfig.Root);

end;


class function TXMLConfig.RegisterFileInfo(AFileInfo: IFileInfo): Boolean;

begin

  Result := Supports(AFileInfo, IFileInfo, AppFileInfo);

end;


procedure TXMLConfig.Save;

begin

  FConfig.SaveToFile(FConfigPath);

end;


{ TConfigNode }


constructor TConfigNode.Create(AXmlNode: TXmlNode);

begin

  inherited Create();

  FXMLNode := AXmlNode;

end;


procedure TConfigNode.DeleteConfig(const AConfig: string);

begin

  FXMLNode.AttributeByName[UTF8Encode(AConfig)].Delete;

end;


destructor TConfigNode.Destroy;

begin

  //这里不能释放Node,需要配合整个XML一起释放,若单独释放,会有意想不到的问题

  FXMLNode := nil;

  inherited;

end;


function TConfigNode.EnmuConfigNames: TStringList;

var

  I: Integer;

begin

  Result := TStringList.Create;

  for I := 0 to FXMLNode.AttributeCount - 1 do

  begin

    Result.Add(FXMLNode.Attributes[i].NameUnicode);

  end;

end;


function TConfigNode.EnmuConfigValues: TStringList;

var

  I: Integer;

begin

  Result := TStringList.Create;

  for I := 0 to FXMLNode.AttributeCount - 1 do

  begin

    Result.Add(FXMLNode.Attributes[i].ValueUnicode);

  end;

end;


function TConfigNode.GetName: string;

begin

  Result := FXMLNode.AttributeValueByNameWide['Name'];

end;


function TConfigNode.GetValueByConfig(AConfig: string): string;

begin

  Result := FXMLNode.AttributeValueByNameWide[UTF8Encode(AConfig)];

end;


procedure TConfigNode.SetValueByConfig(AConfig: string; const Value: string);

var

  AAttribute: TsdAttribute;

begin

  AAttribute := FXMLNode.AttributeByName[UTF8Encode(AConfig)];

  if Assigned(AAttribute) then

  begin

    AAttribute.ValueUnicode := Value;

  end else

  begin

    FXMLNode.AttributeAdd(UTF8Encode(AConfig), UTF8Encode(Value));

  end;

  AAttribute := nil;

end;


function TConfigNode.ValueWithDefault(AConfig, ADefualt: string): string;

begin

  Result := Value[AConfig];

  if Result = EmptyStr then

  begin

    Value[AConfig] := ADefualt;

    Result := ADefualt;

  end;

end;


{ TConfigNodes }


function TConfigNodes.AddConfigNode(AName: string): IConfigNode;

var

  AXmlNode: TXmlNode;

begin

  Result := GetConfigNodeByName(AName);

  if Result = nil then

  begin

    AXmlNode := FRootNode.NodeNew(UTF8Encode(FType));

    AXmlNode.AttributeAdd('Name',UTF8Encode(AName));

    FXmlNodes.Add(AXmlNode);

    Result := TConfigNode.Create(AXmlNode);

  end;

  AXmlNode := nil;

end;


constructor TConfigNodes.Create(const ARootNode: TXmlNode; const AType: string);

var

  I: Integer;

begin

  inherited Create();

  FRootNode := ARootNode;

  FXmlNodes := TList<TXmlNode>.Create;

  FType := AType;

  for I := 0 to ARootNode.ElementCount - 1 do

  begin

    if ARootNode.Elements[i].NameUnicode = AType then

    begin

      FXmlNodes.Add(ARootNode.Elements[i]);

    end;

  end;

end;


procedure TConfigNodes.DeleteConfig(AName: string);

var

  I: Integer;

begin

  for I := 0 to FXmlNodes.Count - 1 do

  begin

    if FXmlNodes[i].AttributeValueByNameWide['Name'] = AName then

    begin

      FXmlNodes[i].Delete;

      FXmlNodes.Delete(i);

      Exit;

    end;

  end;

end;


destructor TConfigNodes.Destroy;

begin

  FreeAndNil(FXmlNodes);

  inherited;

end;


function TConfigNodes.EnmuConfigNodes: TStringList;

var

  I: Integer;

begin

  Result := TStringList.Create;

  for I := 0 to FXmlNodes.Count - 1 do

  begin

    Result.Add(FXmlNodes[i].AttributeValueByNameWide['Name']);

  end;

end;


function TConfigNodes.GetConfigNodeByIndex(AIndex: Integer): IConfigNode;

begin

  Result := TConfigNode.Create(FXmlNodes[AIndex]);

end;


function TConfigNodes.GetConfigNodeByName(AName: string): IConfigNode;

var

  I: Integer;

begin

  Result := nil;

  for I := 0 to FXmlNodes.Count - 1 do

  begin

    if FXmlNodes[i].AttributeValueByNameWide['Name'] = AName then

    begin

      Result := TConfigNode.Create(FXmlNodes[i]);

      Exit;

    end;

  end;

end;


function TConfigNodes.GetConfigNodeCount: Integer;

begin

  Result := FXmlNodes.Count;

end;


{ TRootNode }


constructor TRootNode.Create(AXmlNode: TXmlNode);

begin

  inherited Create();

  FRootNode := AXmlNode;

end;


destructor TRootNode.Destroy;

begin

  // 不能释放,等待随主类释放

  FRootNode := nil;

  inherited;

end;


function TRootNode.GetConfigsByType(AType: string): IConfigNodes;

begin

  Result := TConfigNodes.Create(FRootNode, AType);

end;


end.


因为项目特性,里面有注册FILEINFO的接口,这是我自己项目中的一个全局文件管理类。若大家不需要的话,直接更换成自己的配置文件目录就好了。

调用例子:

procedure TFrm1.Btn1Click(Sender: TObject);

var

  AServerList : TStrings ;

  ILoginInfo: IConfigNode;

begin

  //获取服务器列表

  AServerList := AppServerConfig.Root['AppServer'].EnmuConfigNodes;

  CbxServer.Properties.Items.AddStrings(AServerList);

  FreeAndNil(AServerList);

  ILoginInfo := UserConfig.Root['LoginInfo'].AddConfigNode('Default');

  //读取上次登录的用户名

  TxtUserName.Text := ILoginInfo['LastUser'];

  //读取上次登录的服务器名

  CbxServer.Text := ILoginInfo['LastServer'];

  ILoginInfo := nil;

end;



<?xml encoding="utf-8" version="1.0"?> <ConfigData> <LoginInfo Name="Default" LastUser="Test" LastServer="Test" LastRole=""/> <ReportDlgCfg Name="Default" ShowPrintDlg="0" ShowExportDlg="0" AutoCreateDir="0" OpenFile="0" LastPrinter="Microsoft XPS Document Writer"/> </ConfigData>


阅读48