Jump to content

Recommended Posts

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
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 by Remy Lebeau

Share this post


Link to post

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×