Search code examples
xmldelphixml-rpclazarusfreepascal

XML-RPC with Lazarus freepascal


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.


Solution

  • 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;