NicholasSPI
NicholasSPI

Reputation: 13

Parsing XML data that have elements with the same name in Delphi

I see there are questions asking how to parse XML that have tags with the same name but none show how iterate through. I am pulling XML that is formatted as such

<Documents>
 <a:Document>
  <a:ID>264</a:ID>
  <a:DocumentTitle>Packaged Air-Handling Units</a:DocumentTitle>
 </a:Document>
 <a:Document>
  <a:ID>324</a:ID>
  <a:DocumentTitle>Heater Coil</a:DocumentTitle>
 </a:document>
</Documents>

The code I have to parse is

Procedure EpicParse( ED : TDOMDocument30; Var ET : EpicTag );
Var
  epicNodeList  : IXMLDOMNodelist;
  I             : Integer;

Begin

  epicNodeList  := ED.DefaultInterface.getElementsByTagName( 'a:ID' );
  ET.id         := epicNodeList.item[ 0 ].text;
end;

This will get the first instance of a:ID but how do I set up a for loop get the information from one instance of a:Document then increment and get the info from the next.

Upvotes: 0

Views: 2156

Answers (2)

Ken White
Ken White

Reputation: 125749

There are several ways to do what you've asked. Here's an example of a couple of them, in a button click event handler that populates the content of a plain old TMemo. It uses MSXML, so it only works on Windows. I've commented the code to explain what it does and why.

I've included sample XML based on what you provided (somewhat), because what you posted is invalid. (It contains a namespace alias a: for a namespace you did not include. Also, XML is case sensitive, so </document> is not a closing element for <Document>, and therefore the XML doesn't properly parse.) You'll need to add the namespace alias back in where appropriate once you adapt this to your actual code.

uses
  msxml;

const
  sXML = '<Documents>'#13 +
         '  <Document>'#13 +
         '    <ID>264</ID>'#13 +
         '    <DocumentTitle>Packaged Air-Handling Units</DocumentTitle>'#13 +
         '  </Document>'#13 +
         '  <Document>'#13 +
         '    <ID>324</ID>'#13 +
         '    <DocumentTitle>Heater Coil</DocumentTitle>'#13 +
         '  </Document>'#13 +
         '</Documents>'#13 ;

procedure TForm1.Button1Click(Sender: TObject);
var
  XMLDoc: IXMLDOMDocument;
  NodeList: IXMLDOMNodeList;
  Node, SubNode: IXMLDOMNode;
  i, j: Integer;
begin
  Memo1.Lines.Clear;
  XMLDoc := CoDOMDocument.Create;
  XMLDoc.loadXML(sXML);

  Memo1.Lines.Add('XPath select');
  Memo1.Lines.Add('============');

  // Select all of the Document elements (and their child nodes)
  NodeList := XMLDoc.selectNodes('/Documents/Document');
  for i := 0 to NodeList.length - 1 do
  begin
    // Get each Document node
    Node := NodeList.item[i];
    // Select the ID node
    SubNode := Node.selectSingleNode('ID');
    Memo1.Lines.Add('ID: ' + SubNode.firstChild.Text);
    // Select the DocumentTitle node
    SubNode := Node.selectSingleNode('DocumentTitle');
    Memo1.Lines.Add('DocumentTitle: ' + SubNode.firstChild.Text);
    Memo1.Lines.Add('');
  end;

  Memo1.Lines.Add('Iteration');
  Memo1.Lines.Add('=========');
  // Still select all Document elements using XPath, slightly different expression.
  // Note we really didn't need to do this, because the reference we had
  // before is still valid. Just included as an example.
  NodeList := XMLDoc.selectNodes('//Document');
  for i := 0 to NodeList.length - 1 do
  begin
    // Get each Document node
    Node := NodeList.item[i];
    // Iterate through it's child nodes (ID and DocumentTitle),
    // outputting both the node name and its text content
    for j := 0 to Node.childNodes.length - 1 do
    begin
      SubNode := Node.childNodes[j];
      Memo1.Lines.Add(SubNode.nodeName + ': ' + SubNode.firstChild.text);
    end;
    Memo1.Lines.Add('');
  end;
end;

Upvotes: 1

mjn
mjn

Reputation: 36664

First get the list of Document elements:

myList := ED.DefaultInterface.getElementsByTagName( 'a:Document' );

Then iterate over this lists elements, to collect the ID elements.

Upvotes: 0

Related Questions