unit tlXMLClass;
interface
uses
Classes, XMLIntf,
// it is important!!! the module allows you to work with the properties of the object
TypInfo;
type
TXMLClass = class (TPersistent)
private
// here we will have the name of the file with the settings
FXMLFilePath: string;
// and here is the name of the application for identification
FApplicationName: string;
// name of the root file branch
FRootNodeName: string;
// version
FVersion: byte;
protected
procedure SaveClass (oObject: TObject; Node: IXMLNode);
procedure LoadClass (oObject: TObject; Node: IXMLNode);
public
constructor Create (const AppName, XMLFilePath: string; RootNodeName: string = 'config');
procedure Initialize; abstract;
// load values from file
procedure Load;
// and save them
procedure Save;
// virtual method, we will write it in the heir
procedure LoadDefaults; virtual;
property ApplicationName: string read FApplicationName write FApplicationName;
property RootNodeName: string read FRootNodeName;
property Version: byte read FVersion write FVersion default 1;
end;
implementation
uses
// about XMLDoc and XMLIntf - these two modules appeared in Delphi not so long ago,
// as long as I remember. if you don’t have them, you’ll have to do it in a different way.
XMLDoc, SysUtils, Windows,
resConfig;
{TXMLConfig}
{$ REGION 'Initialization'}
constructor TXMLClass.Create (const AppName, XMLFilePath: string; RootNodeName: string = 'config');
begin
Initialize;
FApplicationName: = AppName;
FXMLFilePath: = XMLFilePath;
FRootNodeName: = RootNodeName;
// set default settings
LoadDefaults;
end;
procedure TXMLClass.LoadDefaults;
begin
end;
{$ ENDREGION}
{$ REGION 'Loading'}
procedure TXMLClass.LoadClass (oObject: TObject; Node: IXMLNode);
// here we try to find the property and set its value
procedure GetProperty (PropInfo: PPropInfo);
var
sValue: string;
TempNode: IXMLNode;
LObject: TObject;
begin
// try to find a branch with the name of the property
TempNode: = Node.ChildNodes.FindNode (PropInfo ^ .Name);
// if not found, then exit the function. property value will remain the default value
if TempNode = nil then
exit;
// if the property is not an object, then we get the value from the branch
if PropInfo ^ .PropType ^ .Kind <> tkClass then
sValue: = TempNode.Text;
// analyze the type of the property and give it a value according to it
case PropInfo ^ .PropType ^ .Kind of
tkEnumeration:
if GetTypeData (PropInfo ^ .PropType ^) ^. BaseType ^ = TypeInfo (Boolean)
then SetPropValue (oObject, PropInfo, Boolean (StrToBool (sValue)))
else SetPropValue (oObject, PropInfo, StrToInt (sValue));
tkInteger, tkChar, tkWChar, tkSet:
SetPropValue (oObject, PropInfo, StrToInt (sValue));
tkFloat:
SetPropValue (oObject, PropInfo, StrToFloat (sValue));
tkString, tkLString, tkWString:
SetPropValue (oObject, PropInfo, sValue);
// but if the property is an object, then recursively execute the procedure
// LoadClass, but for the found branch
tkClass:
begin
LObject: = GetObjectProp (oObject, PropInfo);
if LObject <> nil then
LoadClass (LObject, TempNode);
end;
end;
end;
var
i, iCount: integer;
PropInfo: PPropInfo;
PropList: PPropList;
begin
// get the number of public properties of the object
iCount: = GetTypeData (oObject.ClassInfo) ^. PropCount;
if iCount> 0 then
begin
// request a piece of memory for storage
// property list
GetMem (PropList, iCount * SizeOf (Pointer));
// and get them in PropList
GetPropInfos (oObject.ClassInfo, PropList);
try
// go through the list of properties
for i: = 0 to iCount - 1 do
begin
PropInfo: = PropList ^ [i];
if PropInfo = nil then
break;
// and for each property we execute GetProperty (see above)
GetProperty (PropInfo);
end;
finally
// and at the very end we release the memory occupied by the list
FreeMem (PropList, iCount * SizeOf (Pointer));
end;
end;
end;
procedure TXMLClass.Load;
// procedure for reading from file
var
XMLRoot: IXMLNode;
XML: IXMLDocument;
begin
LoadDefaults;
if not FileExists (FXMLFilePath) then
exit;
try
// xml file with settings
XML: = LoadXMLDocument (FXMLFilePath);
// root branch of the xml document
XMLRoot: = XML.DocumentElement;
// check if this file is ours
if (XMLRoot.NodeName <> FRootNodeName) or
(XMLRoot.Attributes [rsApplication] <> FApplicationName) then
exit;
FVersion: = XMLRoot.Attributes [rsFormat];
// let's go download
LoadClass (Self, XMLRoot);
except
// an exception occurred? load default values
LoadDefaults;
end;
end;
{$ ENDREGION}
{$ REGION 'Saving'}
procedure TXMLClass.SaveClass (oObject: TObject; Node: IXMLNode);
// here we save the values and this procedure is very
// much like the download procedure, so comment
// I'll be here only what is not in that procedure
procedure WriteProperty (PropInfo: PPropInfo);
var
sValue: string;
LObject: TObject;
TempNode: IXMLNode;
begin
case PropInfo ^ .PropType ^ .Kind of
tkEnumeration:
if GetTypeData (PropInfo ^ .PropType ^) ^. BaseType ^ = TypeInfo (Boolean)
then sValue: = BoolToStr (Boolean (GetOrdProp (oObject, PropInfo)), true)
else sValue: = IntToStr (GetOrdProp (oObject, PropInfo));
tkInteger, tkChar, tkWChar, tkSet:
sValue: = IntToStr (GetOrdProp (oObject, PropInfo));
tkFloat:
sValue: = FloatToStr (GetFloatProp (oObject, PropInfo));
tkString, tkLString, tkWString:
sValue: = GetWideStrProp (oObject, PropInfo);
tkClass:
if Assigned (PropInfo ^ .GetProc) and Assigned (PropInfo ^ .SetProc) then
begin
LObject: = GetObjectProp (oObject, PropInfo);
if LObject <> nil then
begin
TempNode: = Node.AddChild (PropInfo ^ .Name);
SaveClass (LObject, TempNode);
end;
end;
end;
// here we create a new branch in the root of the document
// and write the property value to it
if PropInfo ^ .PropType ^ .Kind <> tkClass then
with Node.AddChild (PropInfo ^ .Name) do
Text: = sValue;
end;
var
PropInfo: PPropInfo;
PropList: PPropList;
i, iCount: integer;
begin
iCount: = GetTypeData (oObject.ClassInfo) ^. PropCount;
if iCount> 0 then
begin
GetMem (PropList, iCount * SizeOf (Pointer));
try
GetPropInfos (oObject.ClassInfo, PropList);
for i: = 0 to iCount - 1 do
begin
PropInfo: = PropList ^ [i];
if PropInfo = nil then
Break;
WriteProperty (PropInfo);
end;
finally
FreeMem (PropList, iCount * SizeOf (Pointer));
end;
end;
end;
procedure TXMLClass.Save;
var
FRootNode: IXMLNode;
FBackFileName: string;
XML: IXMLDocument;
begin
// so far without backup. just in case it does not hurt
FBackFileName: = ChangeFileExt (FXMLFilePath, '.bak');
try
// original is deleted
if FileExists (FXMLFilePath) then
DeleteFile (PChar (FXMLFilePath));
try
// create a new XML document
XML: = NewXMLDocument;
// ask him the encoding and version
with XML do
begin
Encoding: = 'UTF-8';
Version: = '1.0';
end;
// add root branch FRootNodeName
FRootNode: = XML.AddChild (FRootNodeName);
FRootNode.Attributes [rsApplication]: = FApplicationName;
FRootNode.Attributes [rsFormat]: = FVersion;
SaveClass (Self, FRootNode);
// save the document
XML.SaveToFile (FXMLFilePath);
except
// but if an error occurred, then we try
// restore the file from the created backup
if FileExists (FBackFileName) then
RenameFile (FBackFileName, FXMLFilePath);
end;
finally
// and at the very end delete the backup
if FileExists (FBackFileName) then
DeleteFile (PChar (FBackFileName));
end;
end;
{$ ENDREGION}
end. <? xml version = "1.0" encoding = "UTF-8"?> <config application = "test" format = "0"> <Main> <HistoryDepth> 40 </ HistoryDepth> </ Main> <LookAndFeel> <WindowWidth> 200 </ WindowWidth> <AlwaysOnTop> True </ AlwaysOnTop> <AlphaBlending> False </ AlphaBlending> <AlphaBlendValue> 245 </ AlphaBlendValue> <AnimateWithAlpha> False </ AnimateWithAlpha> <Elements> <ItemDefault> <Font> <Name> Tahoma </ Name> <Size> 8 </ Size> <Color> 0 </ Color> <Bold> False </ Bold> <Italic> False </ Italic> <Strikeout> False </ Strikeout> <Underline> False </ Underline> </ Font> </ ItemDefault> <ItemChecked> <Font> <Name> Tahoma </ Name> <Size> 8 </ Size> <Color> 9079434 </ Color> <Bold> False </ Bold> <Italic> False </ Italic> <Strikeout> True </ Strikeout> <Underline> False </ Underline> </ Font> </ ItemChecked> </ Elements> </ LookAndFeel> <Confirmation> <DeleteElement> True </ DeleteElement> </ Confirmation> <Windows> <HelpWindow> <Top> 182 </ Top> <Left> 73 </ Left> <Width> 1135 </ Width> <Height> 642 </ Height> <WindowState> 0 </ WindowState> <SplitterLeft> 156 </ SplitterLeft> </ HelpWindow> </ Windows> </ config>
Source: https://habr.com/ru/post/27764/
All Articles