Ole Ekerhovd 0 Posted December 1, 2019 I'm not able to parse an xml file.  I have looked into several code examples online, but I think parsing xml is quite hard to understand 🙂  Code shown is partly from Embarcadero Code Examples.  I'm interested in getting then value/text from the msg-no, sender and receiver nodes.  Any help appreciated.  Regards Ole  This is the xml file <inbox-query-response version="1.0"> <messages> <message>        <self>https://xxxxxxx</self>        <message-meta-data>          <msg-no>9064430</msg-no>          <direction>IN</direction>          <received>2019-11-24T09:00:12.000+01:00</received>          <uuid>15745</uuid>          <peppol-header>            <sender>9908:123</sender>            <receiver>9908:456</receiver>            <channel>busdox-transport-as2-ver1p0</channel>            <document-type>INVOICE</document-type>            <document-id>Long text here</document-id>            <process-name>UNKNOWN</process-name>            <process-id>urn:www.cenbii.eu:profile:bii05:ver2.0</process-id>          </peppol-header>        </message-meta-data> </message> <message>        <self>https://xxxxxxx</self>        <message-meta-data>          <msg-no>9064565</msg-no>          <direction>IN</direction>          <received>2019-11-24T15:00:14.000+01:00</received>          <uuid>15746</uuid>          <peppol-header>            <sender>9908:321</sender>            <receiver>9908:654</receiver>            <channel>busdox-transport-as2-ver1p0</channel>            <document-type>INVOICE</document-type>            <document-id>Long text here</document-id>            <process-name>UNKNOWN</process-name>            <process-id>urn:www.cenbii.eu:profile:bii05:ver2.0</process-id>          </peppol-header>        </message-meta-data> </message> </messages> </inbox-query-response>  This is the code I try to use procedure TForm2.RetrieveDocument; const  CAttrName = 'version'; // This is found  //CAttrName = 'msg-no'; This is not found var  LDocument: IXMLDocument;  LNodeElement, LNode: IXMLNode;  LAttrValue: string;  I: Integer;  begin  LDocument := TXMLDocument.Create(nil);  LDocument.LoadFromFile(SrcPath); { File should exist. }   // Number of childNodes, always shows 1  showmessage(inttostr(Ldocument.ChildNodes.Count));   { Find a specific node. }  LNodeElement := LDocument.ChildNodes.FindNode('inbox-query-response');   if (LNodeElement <> nil) then  begin    { Get a specific attribute. }    if (LNodeElement.HasAttribute(CAttrName)) then    begin      LAttrValue := LNodeElement.Attributes[CAttrName];      showmessage('Attribute value: ' + LAttrValue);    end;     { Traverse child nodes. }    for I := 0 to LNodeElement.ChildNodes.Count - 1 do // Gives only one iteration    begin      LNode := LNodeElement.ChildNodes.Get(I);      { Display node name. }      showmessage('Node name: ' + LNode.NodeName); // Only node, messages, is shown ones      { Check whether the node type is Text. }      if LNode.NodeType = ntText then      begin        showmessage('This is a node of type Text. The text is: ' + LNode.Text);      end;      { Check whether the node is text element. }      if LNode.IsTextElement then      begin        showmessage('This is a text element. The text is: ' + LNode.Text);      end;     end;  end; end; Share this post Link to post
Remy Lebeau 1394 Posted December 2, 2019 (edited) On 12/1/2019 at 8:10 AM, Ole Ekerhovd said: I'm not able to parse an xml file. When you encounter a problem with something, you need to be more specific about WHAT the problem actually is, and WHERE the problem is in the code. It is generally considered impolite to just dump code into a forum and expect people to debug it for you without any idea what is wrong with it. Quote  // Number of childNodes, always shows 1  showmessage(inttostr(Ldocument.ChildNodes.Count)); There is always only 1 top-level element in a document. But there could be XML processing instruction before that element (<?xml ...?>, etc). Quote  { Find a specific node. }  LNodeElement := LDocument.ChildNodes.FindNode('inbox-query-response'); To get the 1st element in a document, you should use the DocumentElement property instead: LNodeElement := LDocument.DocumentElement; Quote    { Traverse child nodes. }    for I := 0 to LNodeElement.ChildNodes.Count - 1 do // Gives only one iteration    begin      LNode := LNodeElement.ChildNodes.Get(I);      { Display node name. }      showmessage('Node name: ' + LNode.NodeName); // Only node, messages, is shown ones As it should be, since <messages> is the only child node of the <inbox-query-response> element. But your code is not going any deeper into the document, so you are not seeing the <message> child elements, and their child elements, and so on. You have to iterate each level of ChildNodes separately. Quote      { Check whether the node type is Text. }      if LNode.NodeType = ntText then That will never be true for the <messages> element that LNode is pointing at. Quote      { Check whether the node is text element. }      if LNode.IsTextElement then Neither will that.  With that said, try something more like this: procedure TForm2.RetrieveDocument; var LDocument: IXMLDocument; LNodeElement, LNode: IXMLNode; LAttrValue: string; I: Integer; begin LDocument := TXMLDocument.Create(nil); LDocument.LoadFromFile(SrcPath); { File should exist. } // Number of childNodes, always shows 1 ShowMessage(IntToStr(LDocument.ChildNodes.Count)); { Find the root element node. } LNodeElement := LDocument.DocumentElement; if LNodeElement <> nil then begin { Get a specific attribute. } if LNodeElement.HasAttribute('version') then begin LAttrValue := LNodeElement.Attributes['version']; ShowMessage('Attribute value: ' + LAttrValue); end; { Find a specific node. } LNodeElement := LNodeElement.ChildNodes.FindNode('messages'); if LNodeElement <> nil then begin { Traverse messages. } for I := 0 to LNodeElement.ChildNodes.Count - 1 do begin LNode := LNodeElement.ChildNodes.Get(I); { Display node name. } ShowMessage('Node name: ' + LNode.NodeName); if LNode.NodeName = 'message' then ProcessMessageElement(LNode); end; end; end; end; procedure TForm2.ProcessMessageElement(const AMessage: IXMLNode); var I: Integer; LNode: IXMLNode; begin { Traverse values. } for I := 0 to AMessage.ChildNodes.Count - 1 do begin LNode := AMessage.ChildNodes.Get(I); { Check whether the node type is Text. } if (LNode.NodeType = ntText) or LNode.IsTextElement then begin ShowMessage(LNode.NodeName + ' is a node of type Text. The text is: ' + LNode.Text); end; if LNode.NodeName = 'message-meta-data' then ProcessMetaDataElement(LNode); end; end; procedure TForm2.ProcessMetaDataElement(const AMetaData: IXMLNode); var I: Integer; LNode: IXMLNode; begin { Traverse values. } for I := 0 to AMetaData.ChildNodes.Count - 1 do begin LNode := AMetaData.ChildNodes.Get(I); { Check whether the node type is Text. } if (LNode.NodeType = ntText) or LNode.IsTextElement then begin ShowMessage(LNode.NodeName + ' is a node of type Text. The text is: ' + LNode.Text); end; if LNode.NodeName = 'peppol-header' then ProcessPeppolHeaderElement(LNode); end; end; procedure TForm2.ProcessPeppolHeaderElement(const AHeader: IXMLNode); var I: Integer; LNode: IXMLNode; begin { Traverse values. } for I := 0 to AHeader.ChildNodes.Count - 1 do begin LNode := AHeader.ChildNodes.Get(I); { Check whether the node type is Text. } if (LNode.NodeType = ntText) or LNode.IsTextElement then begin ShowMessage(LNode.NodeName + ' is a node of type Text. The text is: ' + LNode.Text); end; end; end;  Edited December 2, 2019 by Remy Lebeau Share this post Link to post
Ole Ekerhovd 0 Posted December 3, 2019 Hi Remy  Thanks for your answer, your code example works perfectly.  Your comment on "dump code" is duly noted, it was not my intention.  Regards, Ole Share this post Link to post