XML配置文件读取类[DELPHI]

发现用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>

 

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                                                            }{----------------------------------------------------------------------------}interfaceuses  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;
implementationvar  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.

posted on 2020-08-12 21:52  癫狂编程  阅读(303)  评论(0编辑  收藏  举报

导航

好的代码像粥一样,都是用时间熬出来的