I am writing config data to an XML file. But the XML looks very unstructured or how to say
<Configuration>
<Ftp Host="LOCALHOST" Port="21"/>
<Pop3 Host="LOCALHOST" Port="110" Interval="30000"/>
<Smtp Host="LOCALHOST" Port="25"/>
</Configuration>
I would like it to look like
<Configuration>
<Ftp
Host="LOCALHOST"
Port="21"
/>
<Pop3
Host="LOCALHOST"
Port="110"
Interval="30000"
/>
<Smtp
Host="LOCALHOST"
Port="25"
/>
</Configuration>
Is that possible in any way Here is a snippet of my Delphi code for this. I have functions/procedure for all types but just showing 2 here
constructor TConnXml.Create(const FileName: string);
begin
inherited Create;
fConfigfile := FileName;
fXMLDoc := TXMLDocument.Create(Application);
fXMLDoc.Options := [doNodeAutoIndent];
if FileExists(fConfigfile) then
fXMLDoc.LoadFromFile(fConfigfile)
else
begin
fXMLDoc.Active := True;
fXMLDoc.AddChild('Configuration');
fXMLDoc.SaveToFile(fConfigfile);
end;
end;
constructor TConnXml.Create;
begin
Create(SettingsFileBuild);
end;
function TConnXml.ReadString(const Section, Key, Default: string): string;
var
Node: IXMLNode;
begin
Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
if Assigned(Node) and Node.HasAttribute(Key) then
Result := Node.Attributes[Key]
else
Result := Default;
end;
procedure TConnXml.WriteString(const Section, Key, Value: string);
var
Node: IXMLNode;
begin
if ReadString(Section, Key, '') = Value then
Exit;
Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
if not Assigned(Node) then
Node := fXMLDoc.DocumentElement.AddChild(Section);
Node.Attributes[Key] := Value;
fModified := True;
Save;
end;
procedure TConnXml.Save;
begin
if not fModified then
Exit;
if fBackup then
CopyFile(PChar(fConfigfile), PChar(fConfigfile + '.bak'), False);
fXMLDoc.Active := True;
fXMLDoc.SaveToFile(fConfigfile);
fModified := False;
end;
function TConnXml.ReadBoolean(const Section, Key: string; Default: Boolean): Boolean;
begin
Result := Boolean(ReadInteger(Section, Key, Integer(Default)));
end;
procedure TConnXml.WriteBoolean(const Section, Key: string; Value: Boolean);
begin
WriteInteger(Section, Key, Integer(Value));
end;
If you produce this XML and it is for configuration purposes, then making it more readable has its purpose. I use XML for configuration a lot and I only use attributes when really applicable.
I would write it like this:
<Configuration>
<Ftp>
<Host>LOCALHOST</Host>
<Port>25</Port>
</Ftp>
<Pop3>
<Host>LOCALHOST/<Host>
<Port>110</Port>
<Interval>30000</Interval>
</Pop>
<Smtp>
<Host>LOCALHOST</Host>
<Port>25</Port>
</Smtp>
</Configuration>
Using some other format, then XML, is also a solution. But if you stick to XML then my answer is one way to organize XML in a human readable fasion. Also if you avoid attributes then conversion to JSON for instance is very simple.
Even if XML is bloated with markup I find it readable if structured well. And although it was meant for computer data exchange I find it very good for configuration files. YAML looks fine, but for me it lacks that explicit structure :)
Update:
Due to the request for the code I updated the answer with additional info. To get XML like mine bellow, all you have to do is change one procedure. On the other hand this is basic XML handling so I advise you to learn it.
function TConnXml.ReadString(const Section, Key, Default: string): string;
var
Node: IXMLNode;
Child: IXMLNode;
begin
Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
if not Assigned(Node) then
begin
Result := Default;
Exit;
end;
Child:= Node.FindNode(Key);
if not Assigned(Child) then
begin
Result := Default;
Exit;
end;
Result := Child.Text;
end;
procedure TConnXml.WriteString(const Section, Key, Value: string);
var
Node: IXMLNode;
Child: IXMLNode;
begin
if ReadString(Section, Key, '') = Value then
Exit;
Node := fXMLDoc.DocumentElement.ChildNodes.FindNode(Section);
if not Assigned(Node) then
Node := fXMLDoc.DocumentElement.AddChild(Section);
Child:= Node.ChildNodes.FindNode(Key);
if not Assigned(Child) then
Child:= Node.AddChild(Key);
Child.Text := Value;
fModified := True;
Save;
end;
I wrote it without testing so there may be some mistakes in it, but that is the code you should use.