Lazarus

Programming => General => Topic started by: Vodnik on March 13, 2019, 10:11:17 am

Title: XMLConfig - Saving checklistbox to XML
Post by: Vodnik on March 13, 2019, 10:11:17 am
I have used XMLConfig to store form size and some component values to xml file.
But I got in trouble when I had to save checklistbox to file.
Checklistbox is not fixed, user can add and delete items.
How I would like to see this in XML (checklistbox holds "destinations" items for different DSNs):

Code: XML  [Select][+][-]
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CONFIG>
  3.   <DSN>
  4.     <Name>DSN1</Name>  
  5.     <Include>0</Include>
  6.     <Destination>
  7.       <Number>123456789</Number>
  8.       <Checked>True</Checked>
  9.     </Destination>
  10.     <Destination>
  11.       <Number>234567890</Number>
  12.       <Checked>False</Checked>
  13.     </Destination>
  14.     <Destination>
  15.       <Number>345678901</Number>
  16.       <Checked>True</Checked>
  17.     </Destination>
  18.   </DSN>
  19.   <DSN>
  20.     <Name>DSN2</Name>  
  21.     <Include>2</Include>
  22.     <Destination>
  23.       <Number>456789012</Number>
  24.       <Checked>True</Checked>
  25.     </Destination>
  26.     <Destination>
  27.       <Number>567890123</Number>
  28.       <Checked>False</Checked>
  29.     </Destination>
  30.   </DSN>
  31. </CONFIG>
  32.  

But I can't achieve this with TXMLConfig.

I have hear about TXMLFile which seems to do work in an easy way (https://sourceforge.net/p/xmltreeeditor/wiki/Home/), but I can't find where to get it.

Other components like TXMLDocument seems not easy for the first glance... but of course I will do if there is no simpler way.

So, what is the proper way for solving this task?


Title: Re: XMLConfig - Saving checklistbox to XML
Post by: madref on March 13, 2019, 02:57:53 pm
You already did it


Whats the problem?
If the problem is Booleans then store them as 1's for True and 0's for False
as I read this xml-file
Name: DNS1
Has 3 destionations
Then do a loop for the 3 destinations where you read the number and the boolean


Also store how many DNS's you have first like
Code: XML  [Select][+][-]
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <CONFIG>
  3.   <Total>2</Total>
  4. .....
  5. </CONFIG>
So you know how many loops you have to do in the FOR-Statement
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: Vodnik on March 13, 2019, 05:24:11 pm
@madref, I did this example in text editor. Of course, I can just write/read application data to/from text file, but I would prefer some XML parsing. I can't achieve this result with XMLConfig and can't find XMLFile.
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: lucamar on March 13, 2019, 05:33:14 pm
Why don't you use TXMLPropStorage? It's mostly automatic and much easier to use than XMLConfig; and if you need more control you can always access its XMLConfig property, a TPropStorageXMLConfig--which descends from TXMLConfig
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: Cyrax on March 13, 2019, 08:00:48 pm
TXMLConfig only supports minimal set of XML standard, for speed reasons.

You might want to read this wiki article for more info : http://wiki.freepascal.org/XML_Tutorial
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: Vodnik on March 13, 2019, 08:33:25 pm
@lukamar, you again direct me to txmlpropstorage...
I have successfully tried it and found it useful for basic application settings load/save at program start/exit.

In my application I need more complex processing. User selects DSN (database) and read data from it with some preset values (e.g. list of "destinations"), that are specific for each DSN. List of "destinations" can be edited by user, also items can be checked or unchecked. I would like to save/load checkboxlist specific for each DSN when user selects another DSN.

1) I didn't find a nice example for storing non-fixed-length checklistbox with xmlpropstorage or xmlconfig. This one I don't like very much:
CheckListBox1.Checked[0] := StrToBool(XMLPropStorage1.StoredValue['item_0_checked']);
 
2) xmlpropstorage save/restore properties work on form open/close, isn't it?

3) these components store values in XML attributes, what is not... very graceful in XML. I would like to learn how to build nice XML in Lazarus, because in my future project I plan to deal with REST.

These reasons made me to look for some other possibilities for working with XML. I didn't investigate all features of xmlpropstorage and maybe it can be adopted for my aids. That's why I'm asking here.
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: balazsszekely on March 13, 2019, 08:52:39 pm
Take a look at how units are saved inside an lpi file. It's very similar to what you're trying to achieve.
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: Vodnik on March 13, 2019, 09:41:16 pm
@GetMem, you are right, that's good example.
Though not classic XML, seems it can be done with XMLConfig.
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: jamie on March 13, 2019, 09:42:16 pm
store them as a sting or LongWord.

The string is variable and the LongWord is 32 bit so you can have up to 32 check marks.
you would need an additional tag to indicate the number of bits that are actually in use.

Make a couple of code blocks that process the Check states to build the string or LongWord, what ever you want to use.

Also do the same for the other way round.. etc..

If you write the LongWord out as a string, make sure you always set the  MSB to so it'll included the zero bits.

Title: Re: XMLConfig - Saving checklistbox to XML
Post by: balazsszekely on March 14, 2019, 07:46:38 am
@Vodnik

Please try attached project, I think it's very similar to what you need.
Code: Pascal  [Select][+][-]
  1. type
  2.   TDest = record
  3.     FNumber: String;
  4.     FChecked: Boolean;
  5.   end;
  6.  
  7.   TDNS = record
  8.     FName: String;
  9.     FInclude: Integer;
  10.     FDest: array of TDest;
  11.   end;
  12.  
  13. var
  14.   DNS: array of TDNS;
  15.  
  16. procedure TForm1.SaveToXML(const APath: String);
  17. var
  18.   FXML: TXMLConfig;
  19.   Path, SubPath: String;
  20.   DNSCnt: Integer;
  21.   DestCnt: Integer;
  22.   I, J: Integer;
  23. begin
  24.   FXML := TXMLConfig.CreateClean(APath);
  25.   try
  26.     DNSCnt := Length(DNS);
  27.     FXML.SetDeleteValue('DNSCount/Value', DNSCnt, 0);
  28.     for I := 0 to DNSCnt - 1 do
  29.     begin
  30.       Path := 'DNS' + IntToStr(I) + '/';
  31.       DestCnt := Length(DNS[I].FDest);
  32.       FXML.SetDeleteValue(Path + 'Name', DNS[I].FName, '');
  33.       FXML.SetDeleteValue(Path + 'Include', DNS[I].FInclude, 0);
  34.       FXML.SetDeleteValue(Path + 'DestCnt', DestCnt, 0);
  35.       for J := 0 to DestCnt - 1 do
  36.       begin
  37.         SubPath := Path + 'Destination' +  IntToStr(J) + '/';
  38.         FXML.SetDeleteValue(SubPath + 'Number', DNS[I].FDest[J].FNumber, '');
  39.         FXML.SetDeleteValue(SubPath + 'Checked', DNS[I].FDest[J].FChecked, False);
  40.       end;
  41.     end;
  42.     FXML.Flush;
  43.   finally
  44.     FXML.Free;
  45.   end;
  46. end;
  47.  
  48. procedure TForm1.LoadFromXML(const APath: String);
  49. var
  50.   FXML: TXMLConfig;
  51.   Path, SubPath: String;
  52.   DNSCnt: Integer;
  53.   DestCnt: Integer;
  54.   I, J: Integer;
  55. begin
  56.   if not FileExists(APath) then
  57.     Exit;
  58.   FXML := TXMLConfig.Create(APath);
  59.   try
  60.     DNSCnt := FXML.GetValue('DNSCount/Value', 0);
  61.     SetLength(DNS, DNSCnt);
  62.     for I := 0 to DNSCnt - 1 do
  63.     begin
  64.       Path := 'DNS' + IntToStr(I) + '/';
  65.       DNS[I].FName := FXML.GetValue(Path + 'Name', '');
  66.       DNS[I].FInclude := FXML.GetValue(Path + 'Include', 0);
  67.       DestCnt := FXML.GetValue(Path + 'DestCnt', 0);
  68.       SetLength(DNS[I].FDest, DestCnt);
  69.       for J := 0 to DestCnt - 1 do
  70.       begin
  71.         SubPath := Path + 'Destination' +  IntToStr(J) + '/';
  72.         DNS[I].FDest[J].FNumber := FXML.GetValue(SubPath + 'Number', '');
  73.         DNS[I].FDest[J].FChecked := FXML.GetValue(SubPath + 'Checked', False);
  74.       end;
  75.     end;
  76.   finally
  77.     FXML.Free;
  78.   end;
  79. end;


PS: I did not tested extensively, it may contain bugs.
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: Vodnik on March 14, 2019, 04:12:58 pm
@GetMem, great thanks for this example!
It works.
Seems it will solve my problem.
For me it is an excellent example of nice programming.

You are using Laz2_XMLCfg (UTF-8 version of XMLCfg). Then what is XMLConf - old deprecated module?

The only thing is that I have to save DNS1 and DNS2 separately, in different parts of code. E.g. button 1 saves only settings for DSN1, leaving DSN2 as it is. DSN3 may be added by user later. I would like to adopt your example by myself, but I didn't find any description for CreateClean, SetDeleteValue and GetValue. So I have to play a bit.
Title: Re: XMLConfig - Saving checklistbox to XML
Post by: balazsszekely on March 14, 2019, 06:28:45 pm
@Vodnik
You're welcome. If you need help please let me know.
TinyPortal © 2005-2018