Recent

Author Topic: Problem reading from XMLDoc  (Read 367 times)

RedOctober

  • Sr. Member
  • ****
  • Posts: 268
Problem reading from XMLDoc
« on: September 09, 2019, 10:08:16 pm »
PLATFORM:

Lazarus 1.8.4, indyHTTP 10.6.2.0, OS: Windows Server 2016 (latest updates)

BACKGROUND:

I am converting an old Delphi XE3 project to Lazarus.  The Delphi XE3 project is able to get XML data from Data 24/7 and CDyne via HTTPS using Indy.  All works correctly.

PROBLEM:

I am unable to read specific nodes.  For example, I want to read the value of the "status" node.  Can't do it.

I am following the example here: https://wiki.freepascal.org/XML_Tutorial
I have loaded the XMLDoc as follows:  (Assume all try..excepts and try..finally 's and .Free 's are in place)
Code: Pascal  [Select]
  1.  
  2. uses
  3.     Classes, SysUtils, StdCtrls, Forms, Controls,
  4.     idHTTP, idSSLOpenSSL, idURI,
  5.     DOM, laz2_DOM, laz2_XMLRead, laz2_XMLWrite, laz2_XMLCfg, laz2_XMLUtils, XMLStreaming, ...;
  6.  
  7.  
  8.  
  9. var
  10.   XMLNode: TDOMNode;
  11.   XMLDoc: TXMLDocument;
  12.   xml_mem_strm: TMemoryStream;  
  13.   str_strm: TStringStream;
  14.  
  15. begin
  16. ...
  17.     xml_mem_strm := TMemoryStream.Create;
  18.     str_strm := TStringStream.Create('');
  19. ...
  20.    xHTTP.Get(GlobalVars.TxtMsg.https_get_cmd, xml_mem_strm);
  21.    xml_mem_strm.Position := 0;
  22.  
  23.    ReadXMLFile(XMLDoc, xml_mem_strm);
  24.  
  25.  

This line converts the memory stream to a string and reveals that ALL the data EXISTS in the returned memory stream.

Code: Pascal  [Select]
  1.     prc_log.Lines.Add(MemoryStreamToString(xml_mem_strm));
  2.  

The resulting string from the above line is:

Code: Pascal  [Select]
  1. <?xml version="1.0"?><response><results><result item="1"><status>OK</status><number>15555555555</number><wless>n</wless><carrier_name>Telus Communications, Inc.</carrier_name><carrier_id>50106</carrier_id><sms_address> </sms_address><mms_address> </mms_address></result></results></response>
  2.  

HERE IS WHAT I HAVE TRIED:

Code: Pascal  [Select]
  1.  
  2.     // Status Node  Attempt 1  - Fails
  3.     idx := prc_log.Lines.Add('Selecting Status Node...');
  4.     Application.ProcessMessages;
  5.     XMLNode := XMLDoc.DocumentElement.FindNode('results/result(item=1)/status');  
  6.     if not Assigned(XMLNode) then
  7.       begin
  8.         prc_log.Lines[idx] := prc_log.Lines[idx] + 'Can''t find it. Exiting now.';
  9.         snd_err := True;
  10.         Application.ProcessMessages;
  11.         Exit;
  12.       end;
  13.     GlobalVars.TxtMsg.xml_status := XMLNode.FirstChild.NodeValue;
  14.     prc_log.Lines[idx] := prc_log.Lines[idx] + 'Got it: '  + GlobalVars.TxtMsg.xml_status;
  15.     Application.ProcessMessages;
  16.  
  17.     // Status Node  Attempt 2 - Fails
  18.     idx := prc_log.Lines.Add('Selecting Status Node...');
  19.     Application.ProcessMessages;
  20.     XMLNode := XMLDoc.DocumentElement.FindNode('/response/results/result[@item=''1'']/status');    
  21.     if not Assigned(XMLNode) then
  22.       begin
  23.         prc_log.Lines[idx] := prc_log.Lines[idx] + 'Can''t find it. Exiting now.';
  24.         snd_err := True;
  25.         Application.ProcessMessages;
  26.         Exit;
  27.       end;
  28.     GlobalVars.TxtMsg.xml_status := XMLNode.FirstChild.NodeValue;
  29.     prc_log.Lines[idx] := prc_log.Lines[idx] + 'Got it: '  + GlobalVars.TxtMsg.xml_status;
  30.     Application.ProcessMessages;
  31.  
  32.     // Status Node  Attempt 3 - Fails
  33.     idx := prc_log.Lines.Add('Selecting Status Node...');
  34.     Application.ProcessMessages;
  35.     XMLNode := XMLDoc.DocumentElement.FindNode('status');    
  36.     if not Assigned(XMLNode) then
  37.       begin
  38.         prc_log.Lines[idx] := prc_log.Lines[idx] + 'Can''t find it. Exiting now.';
  39.         snd_err := True;
  40.         Application.ProcessMessages;
  41.         Exit;
  42.       end;
  43.     GlobalVars.TxtMsg.xml_status := XMLNode.FirstChild.NodeValue;
  44.     prc_log.Lines[idx] := prc_log.Lines[idx] + 'Got it: '  + GlobalVars.TxtMsg.xml_status;
  45.     Application.ProcessMessages;
  46.  
  47.  


MY QUESTION:

What is the proper syntax to pull out the various node values, by using their node name, from the XML in the XMLDoc?

Thanks in advance.



wp

  • Hero Member
  • *****
  • Posts: 6137
Re: Problem reading from XMLDoc
« Reply #1 on: September 09, 2019, 11:34:30 pm »
FindNode does not search recursively, only the direct children of a node. But you can easily extend it (there are probably better ways, but this one came to my mind first):

Code: Pascal  [Select]
  1. var
  2.   path: String;       // path of nodes, e.g. "ProjectOptions/BuildModes/Item1"
  3.   sa: TStringArray;
  4.   node: TDOMNode;
  5.   i: Integer;
  6. ...
  7.   sa := path.Split('/');  
  8.   node := doc.DocumentElement;
  9.   i := 0;
  10.   while (node <> nil) and (i < Length(sa)) do
  11.   begin
  12.     node := node.FindNode(sa[i]);
  13.     inc(i);
  14.   end;
  15.   if node <> nil then
  16.   begin
  17.      // found...
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

RedOctober

  • Sr. Member
  • ****
  • Posts: 268
Re: Problem reading from XMLDoc
« Reply #2 on: September 10, 2019, 01:35:26 am »
Hi wp,

I have downloaded your example and it works.  However, I am now stuck trying to figure out the syntax for my "Telco" xml file using your example. Here is the xml structure:

Code: Pascal  [Select]
  1. <?xml version="1.0"?>
  2. <response>
  3.   <results>
  4.     <result item="1">
  5.       <status>OK</status>
  6.       <number>15555555555</number>
  7.       <wless>n</wless>
  8.       <carrier_name>Telus Communications, Inc.</carrier_name>
  9.       <carrier_id>50106</carrier_id>
  10.       <sms_address> </sms_address>
  11.       <mms_address> </mms_address>
  12.     </result>
  13.   </results>
  14. </response>

Here is what I have tried, none of these returns the status value, which should be "OK".  I just get "NOT FOUND'

response/results/result/status
response/results/result[@item=''1'']/status
response/results/result item=''1"/status
response/results/result/status

What is the correct syntax to use, to get the "OK" from the status node?

Thanks in advance.









dsiders

  • Full Member
  • ***
  • Posts: 217
Re: Problem reading from XMLDoc
« Reply #3 on: September 10, 2019, 02:06:16 am »
Here is what I have tried, none of these returns the status value, which should be "OK".  I just get "NOT FOUND'

response/results/result/status
response/results/result[@item=''1'']/status
response/results/result item=''1"/status
response/results/result/status

What is the correct syntax to use, to get the "OK" from the status node?

Thanks in advance.

Those are XPath expressions, so you can use:

/response/results/result[@item="1"]/status[text()] or
/response/results/result[1]/status[text()]
Lazarus 2.0.2 / FPC 3.0.4 / Windows 8.1 64-bit

RedOctober

  • Sr. Member
  • ****
  • Posts: 268
Re: Problem reading from XMLDoc
« Reply #4 on: September 10, 2019, 02:09:42 am »
Hmmm.. neither of these work ....

bytebites

  • Full Member
  • ***
  • Posts: 193
Re: Problem reading from XMLDoc
« Reply #5 on: September 10, 2019, 02:30:50 pm »
Hi wp,

I have downloaded your example and it works.  However, I am now stuck trying to figure out the syntax for my "Telco" xml file using your example. Here is the xml structure:

Code: Pascal  [Select]
  1. <?xml version="1.0"?>
  2. <response>
  3.   <results>
  4.     <result item="1">
  5.       <status>OK</status>
  6.       <number>15555555555</number>
  7.       <wless>n</wless>
  8.       <carrier_name>Telus Communications, Inc.</carrier_name>
  9.       <carrier_id>50106</carrier_id>
  10.       <sms_address> </sms_address>
  11.       <mms_address> </mms_address>
  12.     </result>
  13.   </results>
  14. </response>

Here is what I have tried, none of these returns the status value, which should be "OK".  I just get "NOT FOUND'

response/results/result/status
response/results/result[@item=''1'']/status
response/results/result item=''1"/status
response/results/result/status

What is the correct syntax to use, to get the "OK" from the status node?

Thanks in advance.

Try without root: results/result/status

RedOctober

  • Sr. Member
  • ****
  • Posts: 268
Re: Problem reading from XMLDoc
« Reply #6 on: September 10, 2019, 05:39:41 pm »
Works now.  Since the status node has no attributes and I just wanted the text, I used:   

Code: Pascal  [Select]
  1.   Listbox1.Items.Add(XMLNode.TextContent);
  2.  

to display the text "OK".  Thanks for your detailed help on this.  Project is back on track now.

wp

  • Hero Member
  • *****
  • Posts: 6137
Re: Problem reading from XMLDoc
« Reply #7 on: September 10, 2019, 06:35:45 pm »
Sorry, I did not see the xml sample of your first post.

Glad that it works now. But you will have a problem when there are several "result" nodes with different "item" attributes, e.g.
Code: XML  [Select]
  1. <?xml version="1.0"?>
  2. <response>
  3.   <results>
  4.     <result item="1">
  5.       <status>OK</status>
  6.     </result>
  7.     <result item="2">
  8.       <status>OK</status>
  9.     </result>
  10.   </results>
  11. </response>

My code will always return only the first one...

The attached modified version iterates through all child nodes of "results" until it finds the one with the attribute 'item="1"'.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

RedOctober

  • Sr. Member
  • ****
  • Posts: 268
Re: Problem reading from XMLDoc
« Reply #8 on: September 10, 2019, 09:24:13 pm »
I'm working with CDYNE and Data 24/7.  The HTTP requests I make always result in the same output, so your code with my mods will always work.  Thanks for the help.