XML配置文件读取类[DELPHI]

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

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

废话不多说,上代码!

  1 unit XMLConfig;
  2 {----------------------------------------------------------------------------}
  3 { 这个单元用来处理XML配置文件,对配置文件格式有默认要求                             }
  4 { 格式为,只允许有一个root,然后root下对应配置文件,                               }
  5 { 所有配置,均使用xml属性存取配置,属性中必须存在Name属性,                         }
  6 { 不得单独使用下级Node                                                         }
  7 { PS: 使用NativeXML库作为XML取数基本集,NativeXML请自行获取                      }
  8 { By Raymond.Zhang @ 2012.07.12 Mail: Acni.ray@gmail.com                     }
  9 { Tebs Work Group                                                            }
 10 {----------------------------------------------------------------------------}
 11 interface
 12 uses
 13   NativeXml, System.Classes, System.SysUtils, CommLib,
 14   System.Generics.Collections;
 15 
 16 type
 17 
 18   //为了自动释放的特性,使用接口
 19   {$REGION 'Interface'}
 20   IConfigNode = interface
 21     ['{67323F7D-9E6C-420B-BF1C-92457D829380}']
 22     function EnmuConfigNames: TStringList;
 23     function EnmuConfigValues: TStringList;
 24     function GetName: string;
 25     function GetValueByConfig(AConfig: string): string;
 26     function ValueWithDefault(AConfig: string; ADefualt: string):string;
 27     procedure DeleteConfig(const AConfig: string);
 28     procedure SetValueByConfig(AConfig: string; const Value: string);
 29     property Value[AConfig: string]: string read GetValueByConfig write SetValueByConfig; default;
 30     property Name: string read GetName;
 31   end;
 32 
 33   IConfigNodes = interface
 34     ['{56DBB6F5-BD64-4F07-A949-300877B1B787}']
 35     function AddConfigNode(AName: string): IConfigNode;
 36     function EnmuConfigNodes: TStringList;
 37     function GetConfigNodeByIndex(AIndex: Integer): IConfigNode;
 38     function GetConfigNodeByName(AName: string): IConfigNode;
 39     function GetConfigNodeCount: Integer;
 40     procedure DeleteConfig(AName: string);
 41     property ConfigNode[AName: string]: IConfigNode read GetConfigNodeByName; default;
 42     property Count: Integer read GetConfigNodeCount;
 43     property Nodes[AIndex: Integer]: IConfigNode read GetConfigNodeByIndex;
 44   end;
 45 
 46   IRootNode = interface
 47     ['{65213F85-0804-4FE1-A726-CFC0F082AC93}']
 48     function GetConfigsByType(AType: string): IConfigNodes;
 49     property Configs[AType: string]: IConfigNodes read GetConfigsByType; default;
 50   end;
 51   {$ENDREGION}
 52 
 53   TConfigNode = class(TInterfacedObject, IConfigNode)
 54   private
 55     FXMLNode: TXmlNode;
 56     function GetName: string;
 57   protected
 58     function GetValueByConfig(AConfig: string): string;
 59     procedure SetValueByConfig(AConfig: string; const Value: string);
 60   public
 61     constructor Create(AXmlNode: TXmlNode);
 62     destructor Destroy; override;
 63     function EnmuConfigNames: TStringList;
 64     function EnmuConfigValues: TStringList;
 65     function ValueWithDefault(AConfig: string; ADefualt: string):string;
 66     procedure DeleteConfig(const AConfig: string);
 67     property Value[AConfig: string]: string read GetValueByConfig write SetValueByConfig; default;
 68     property Name: string read GetName;
 69   end;
 70 
 71   TConfigNodes = class(TInterfacedObject, IConfigNodes)
 72   private
 73     FType: string;
 74     FRootNode: TXmlNode;
 75     FXmlNodes: TList<TXmlNode>;
 76   protected
 77     function GetConfigNodeByIndex(AIndex: Integer): IConfigNode;
 78     function GetConfigNodeByName(AName: string): IConfigNode;
 79     function GetConfigNodeCount: Integer;
 80   public
 81     constructor Create(const ARootNode: TXmlNode; const AType: string);
 82     destructor Destroy; override;
 83     function AddConfigNode(AName: string): IConfigNode;
 84     function EnmuConfigNodes: TStringList;
 85     procedure DeleteConfig(AName: string);
 86     property ConfigNode[AName: string]: IConfigNode read GetConfigNodeByName; default;
 87     property Count: Integer read GetConfigNodeCount;
 88     property Nodes[AIndex: Integer]: IConfigNode read GetConfigNodeByIndex;
 89   end;
 90 
 91   TRootNode = class(TInterfacedObject, IRootNode)
 92   private
 93     FRootNode: TXmlNode;
 94   public
 95     constructor Create(AXmlNode: TXmlNode);
 96     destructor Destroy; override;
 97     function GetConfigsByType(AType: string): IConfigNodes;
 98   end;
 99 
100   TXMLConfig = class(TObject)
101   private
102     FAutoSave: Boolean;
103     FConfig: TNativeXml;
104     FConfigName: string;
105     FConfigPath: string;
106   protected
107     function GetRoot:IRootNode;
108   public
109     class function RegisterFileInfo(AFileInfo: IFileInfo): Boolean;
110     constructor Create(ConfigName: string);
111     destructor Destroy; override;
112     procedure Save;
113     property Root: IRootNode read GetRoot;
114     property AutoSave: Boolean read FAutoSave write FAutoSave;
115   end;
116 
117 implementation
118 var
119   AppFileInfo: IFileInfo = nil;
120 const
121   ConfigExt: string = '.config';
122   UnRegFileInfo: string = '文件接口未注册,无法获取配置文件路径!';
123 
124 { TXMLConfig }
125 
126 constructor TXMLConfig.Create(ConfigName: string);
127 begin
128   if Assigned(AppFileInfo) then
129   begin
130     inherited Create;
131     FConfigName := ConfigName;
132     FConfigPath := AppFileInfo.ConfigPath + ConfigName + ConfigExt;
133     FConfig := TNativeXml.Create(nil);
134     FConfig.Charset := 'utf-8';
135     FConfig.XmlFormat := xfReadable;
136     FAutoSave := True;
137     if FileExists(FConfigPath) then
138       FConfig.LoadFromFile(FConfigPath)
139     else begin
140       FConfig.VersionString := '1.0';
141       FConfig.Root.Name := 'ConfigData';
142       Save;
143     end;
144   end else
145     raise ERayException.Create(UnRegFileInfo);
146 end;
147 
148 destructor TXMLConfig.Destroy;
149 begin
150   if FAutoSave then Save;
151   FreeAndNil(FConfig);
152   inherited;
153 end;
154 
155 function TXMLConfig.GetRoot: IRootNode;
156 begin
157   Result := TRootNode.Create(FConfig.Root);
158 end;
159 
160 class function TXMLConfig.RegisterFileInfo(AFileInfo: IFileInfo): Boolean;
161 begin
162   Result := Supports(AFileInfo, IFileInfo, AppFileInfo);
163 end;
164 
165 procedure TXMLConfig.Save;
166 begin
167   FConfig.SaveToFile(FConfigPath);
168 end;
169 
170 { TConfigNode }
171 
172 constructor TConfigNode.Create(AXmlNode: TXmlNode);
173 begin
174   inherited Create();
175   FXMLNode := AXmlNode;
176 end;
177 
178 procedure TConfigNode.DeleteConfig(const AConfig: string);
179 begin
180   FXMLNode.AttributeByName[UTF8Encode(AConfig)].Delete;
181 end;
182 
183 destructor TConfigNode.Destroy;
184 begin
185   //这里不能释放Node,需要配合整个XML一起释放,若单独释放,会有意想不到的问题
186   FXMLNode := nil;
187   inherited;
188 end;
189 
190 function TConfigNode.EnmuConfigNames: TStringList;
191 var
192   I: Integer;
193 begin
194   Result := TStringList.Create;
195   for I := 0 to FXMLNode.AttributeCount - 1 do
196   begin
197     Result.Add(FXMLNode.Attributes[i].NameUnicode);
198   end;
199 end;
200 
201 function TConfigNode.EnmuConfigValues: TStringList;
202 var
203   I: Integer;
204 begin
205   Result := TStringList.Create;
206   for I := 0 to FXMLNode.AttributeCount - 1 do
207   begin
208     Result.Add(FXMLNode.Attributes[i].ValueUnicode);
209   end;
210 end;
211 
212 function TConfigNode.GetName: string;
213 begin
214   Result := FXMLNode.AttributeValueByNameWide['Name'];
215 end;
216 
217 function TConfigNode.GetValueByConfig(AConfig: string): string;
218 begin
219   Result := FXMLNode.AttributeValueByNameWide[UTF8Encode(AConfig)];
220 end;
221 
222 procedure TConfigNode.SetValueByConfig(AConfig: string; const Value: string);
223 var
224   AAttribute: TsdAttribute;
225 begin
226   AAttribute := FXMLNode.AttributeByName[UTF8Encode(AConfig)];
227   if Assigned(AAttribute) then
228   begin
229     AAttribute.ValueUnicode := Value;
230   end else
231   begin
232     FXMLNode.AttributeAdd(UTF8Encode(AConfig), UTF8Encode(Value));
233   end;
234   AAttribute := nil;
235 end;
236 
237 function TConfigNode.ValueWithDefault(AConfig, ADefualt: string): string;
238 begin
239   Result := Value[AConfig];
240   if Result = EmptyStr then
241   begin
242     Value[AConfig] := ADefualt;
243     Result := ADefualt;
244   end;
245 end;
246 
247 { TConfigNodes }
248 
249 function TConfigNodes.AddConfigNode(AName: string): IConfigNode;
250 var
251   AXmlNode: TXmlNode;
252 begin
253   Result := GetConfigNodeByName(AName);
254   if Result = nil then
255   begin
256     AXmlNode := FRootNode.NodeNew(UTF8Encode(FType));
257     AXmlNode.AttributeAdd('Name',UTF8Encode(AName));
258     FXmlNodes.Add(AXmlNode);
259     Result := TConfigNode.Create(AXmlNode);
260   end;
261   AXmlNode := nil;
262 end;
263 
264 constructor TConfigNodes.Create(const ARootNode: TXmlNode; const AType: string);
265 var
266   I: Integer;
267 begin
268   inherited Create();
269   FRootNode := ARootNode;
270   FXmlNodes := TList<TXmlNode>.Create;
271   FType := AType;
272   for I := 0 to ARootNode.ElementCount - 1 do
273   begin
274     if ARootNode.Elements[i].NameUnicode = AType then
275     begin
276       FXmlNodes.Add(ARootNode.Elements[i]);
277     end;
278   end;
279 end;
280 
281 procedure TConfigNodes.DeleteConfig(AName: string);
282 var
283   I: Integer;
284 begin
285   for I := 0 to FXmlNodes.Count - 1 do
286   begin
287     if FXmlNodes[i].AttributeValueByNameWide['Name'] = AName then
288     begin
289       FXmlNodes[i].Delete;
290       FXmlNodes.Delete(i);
291       Exit;
292     end;
293   end;
294 end;
295 
296 destructor TConfigNodes.Destroy;
297 begin
298   FreeAndNil(FXmlNodes);
299   inherited;
300 end;
301 
302 function TConfigNodes.EnmuConfigNodes: TStringList;
303 var
304   I: Integer;
305 begin
306   Result := TStringList.Create;
307   for I := 0 to FXmlNodes.Count - 1 do
308   begin
309     Result.Add(FXmlNodes[i].AttributeValueByNameWide['Name']);
310   end;
311 end;
312 
313 function TConfigNodes.GetConfigNodeByIndex(AIndex: Integer): IConfigNode;
314 begin
315   Result := TConfigNode.Create(FXmlNodes[AIndex]);
316 end;
317 
318 function TConfigNodes.GetConfigNodeByName(AName: string): IConfigNode;
319 var
320   I: Integer;
321 begin
322   Result := nil;
323   for I := 0 to FXmlNodes.Count - 1 do
324   begin
325     if FXmlNodes[i].AttributeValueByNameWide['Name'] = AName then
326     begin
327       Result := TConfigNode.Create(FXmlNodes[i]);
328       Exit;
329     end;
330   end;
331 end;
332 
333 function TConfigNodes.GetConfigNodeCount: Integer;
334 begin
335   Result := FXmlNodes.Count;
336 end;
337 
338 { TRootNode }
339 
340 constructor TRootNode.Create(AXmlNode: TXmlNode);
341 begin
342   inherited Create();
343   FRootNode := AXmlNode;
344 end;
345 
346 destructor TRootNode.Destroy;
347 begin
348   // 不能释放,等待随主类释放
349   FRootNode := nil;
350   inherited;
351 end;
352 
353 function TRootNode.GetConfigsByType(AType: string): IConfigNodes;
354 begin
355   Result := TConfigNodes.Create(FRootNode, AType);
356 end;
357 
358 end.

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

调用例子:

 1 procedure TFrm1.Btn1Click(Sender: TObject);
 2 var
 3   AServerList : TStrings ;
 4   ILoginInfo: IConfigNode;
 5 begin
 6   //获取服务器列表
 7   AServerList := AppServerConfig.Root['AppServer'].EnmuConfigNodes;
 8   CbxServer.Properties.Items.AddStrings(AServerList);
 9   FreeAndNil(AServerList);
10   ILoginInfo := UserConfig.Root['LoginInfo'].AddConfigNode('Default');
11   //读取上次登录的用户名
12   TxtUserName.Text := ILoginInfo['LastUser'];
13   //读取上次登录的服务器名
14   CbxServer.Text := ILoginInfo['LastServer'];
15   ILoginInfo := nil;
16 end;

配置文件样式:

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