I know there are plenty of freepascal xml tutorials and posts, but nothing I found so far seem to do the job for me. I am building Lazarus desktop app to connect through API to Odoo, get some data, process and respond. The structure of xml response is quite difficult (at least to me) to work with. The example response is:
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>
<struct>
<member>
<name>create_date</name>
<value>
<string>2016-03-30 09:05:23</string>
</value>
</member>
<member>
<name>file_name</name>
<value>
<string>O156AP000100</string>
</value>
</member>
<member>
<name>dispatch_date</name>
<value>
<string>2016-04-04</string>
</value>
</member>
<member>
<name>height</name>
<value>
<int>0</int>
</value>
</member>
<member>
<name>custom_option_mapping_ids</name>
<value>
<array>
<data />
</array>
</value>
</member>
<member>
<name>message_ids</name>
<value>
<array>
<data />
</array>
</value>
</member>
<member>
<name>message_summary</name>
<value>
<string />
</value>
</member>
<member>
<name>create_uid</name>
<value>
<array>
<data>
<value>
<int>7</int>
</value>
<value>
<string>My 1st years</string>
</value>
</data>
</array>
</value>
</member>
<member>
<name>display_name</name>
<value>
<string>artwork.job,283</string>
</value>
</member>
<member>
<name>message_is_follower</name>
<value>
<boolean>1</boolean>
</value>
</member>
<member>
<name>production_date</name>
<value>
<boolean>0</boolean>
</value>
</member>
<member>
<name>message_last_post</name>
<value>
<boolean>0</boolean>
</value>
</member>
<member>
<name>id</name>
<value>
<int>283</int>
</value>
</member>
<member>
<name>width</name>
<value>
<int>0</int>
</value>
</member>
<member>
<name>file_path</name>
<value>
<string>2016-04-04/Table Rate/1</string>
</value>
</member>
<member>
<name>text_colour</name>
<value>
<array>
<data>
<value>
<int>489</int>
</value>
<value>
<string>Text Colour: Fuchsia Pink</string>
</value>
</data>
</array>
</value>
</member>
<member>
<name>text_lines</name>
<value>
<array>
<data>
<value>
<int>1686</int>
</value>
<value>
<int>16380</int>
</value>
</data>
</array>
</value>
</member>
<member>
<name>sale_order_id</name>
<value>
<array>
<data>
<value>
<int>368</int>
</value>
<value>
<string>SO156</string>
</value>
</data>
</array>
</value>
</member>
<member>
<name>text_font</name>
<value>
<array>
<data>
<value>
<int>492</int>
</value>
<value>
<string>Font: Verdana</string>
</value>
</data>
</array>
</value>
</member>
</struct>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>
The problem is that the values are without unique tags, or ID's. What I am trying to do is to access values by their names. The problem I am facing is that there are no ID's for the values etc, and some parts are arrays. I have been trying with
variables:= Doc.DocumentElement.GetElementsByTagName('member');
fname:=variables[3].FindNode('value').TextContent;
but this will fail if structure changes and doesn't really take arrays into account. Any help will be appreciated.
After all I have found an answer myself. Thx anyone who contributed. I know if it's not the most elegant way of doing this, it does the job though. I made to separate functions running through the xml, onc returning string, other one array.
type
TStringArray = array of string;
...
public
function extractVar(Doc1: TXMLDocument; searchvar:string): String;
function extractArray(Doc1: TXMLDocument; searchvar:string): TStringArray;
...
var
textLines: TStringArray;
...
function TAThread.extractArray(Doc1: TXMLDocument; searchvar:string): TStringArray;
var
c,v:integer;
Name,value:string;
myNodes: TDOMNodeList;
subNodes: TDOMNodeList;
begin
c:=0;
try
myNodes:= Doc1.DocumentElement.GetElementsByTagName('member');
for c:=0 to myNodes.Count-1 do
begin
Name:= myNodes[c].FindNode('name').TextContent;
if (Name=searchvar) then begin
try
subNodes:= myNodes[c].GetChildNodes;
subNodes:= subNodes[1].GetChildNodes;
subNodes:= subNodes[0].GetChildNodes;
subNodes:= subNodes[0].GetChildNodes;
v:=0;
SetLength(Result, subNodes.Count);
for v:=0 to subNodes.Count-1 do
begin
Result[v]:= subNodes[v].TextContent;
end;
finally
subNodes.Free;
end;
end;
end;
finally
myNodes.Free;
end;
end;
function TAThread.extractVar(Doc1: TXMLDocument; searchvar:string): String;
var
c:integer;
Name:string;
myNodes: TDOMNodeList;
begin
c:=0;
try
myNodes:= Doc1.DocumentElement.GetElementsByTagName('member');
for c:=0 to myNodes.Count-1 do
begin
Name:= myNodes[c].FindNode('name').TextContent;
if (Name=searchvar) then begin
result:= myNodes[c].FindNode('value').TextContent;
end;
end;
finally
myNodes.Free;
end;
end;