XML, NextSibling i doNodeAutoIndent. Empty (#9) nodes problem

341 Views Asked by At

My code is:

  XmlDoc := NewXMLDocument;
  with XmlDoc do
    Options := Options + [doNodeAutoIndent];
[...]
  N := Parent.ChildNodes.FindNode('KONTRAHENT');
  while N <> nil do
  begin
    if N.ChildNodes['ID_KONTRAHENTA'].NodeValue = KontrNr then
      DoSomething;
    N := N.NextSibling;
  end;

and XML like this:

<KARTOTEKA_KONTRAHENTOW>
   <KONTRAHENT>
      <ID_KONTRAHENTA>925</ID_KONTRAHENTA>
   </KONTRAHENT>
   <KONTRAHENT>
      <ID_KONTRAHENTA>1208</ID_KONTRAHENTA>
   </KONTRAHENT>
</KARTOTEKA_KONTRAHENTOW>

data viewed in Watch Parent.XML looking that:

'<KARTOTEKA_KONTRAHENTOW>'#$D#$A#9#9'<KONTRAHENT>'#$D#$A#9#9#9'<ID_KONTRAHENTA>925</ID_KONTRAHENTA>'#$D#$A#9#9'</KONTRAHENT>'#$D#$A#9#9'<KONTRAHENT>'#$D#$A#9#9#9'<ID_KONTRAHENTA>1208</ID_KONTRAHENTA>'#$D#$A#9#9'</KONTRAHENT>'#$D#$A#9'</KARTOTEKA_KONTRAHENTOW>'

And when i read nodes in loop, when Options: [doNodeAutoIndent] is set then i have some nodes like this: N = '#$D#$A#9#9' and Count of Nodes = 7 (instead 2, in this example)

Without doNodeAutoIndent, all is OK and Count of Nodes = 2, but my XML file is in one line :(

The question is: How to bypass empty NextSibling when doNodeAutoIndent enabled?

1

There are 1 best solutions below

1
On BEST ANSWER

What you are describing is perfectly normal. Whitespace between elements is treated as additional text nodes within the hierarchy, eg for your example XML, its DOM tree looks like the following:

KARTOTEKA_KONTRAHENTOW
|_ '#$D#$A#9#9'
|_ KONTRAHENT
|  |_ '#$D#$A#9#9#9'
|  |_ ID_KONTRAHENTA
|  |  |_ '925'
|  |_ '#$D#$A#9#9'
|_ '#$D#$A#9#9'
|_ KONTRAHENT
|  |_ '#$D#$A#9#9#9'
|  |_ ID_KONTRAHENTA
|  |  |_ '1208'
|  |_ '#$D#$A#9#9'
|_ '#$D#$A#9'

That is why the node count is higher than you are expecting.

You need to account for these extra text nodes while looping though the elements, eg:

N := Parent.ChildNodes.First;
while N <> nil do
begin
  if (N.NodeType = ntElement) and (N.NodeName = 'KONTRAHENT') then
  begin
    if N.ChildNodes['ID_KONTRAHENTA'].NodeValue = KontrNr then
    begin
      // Do something with N ...
    end;
  end;
  N := N.NextSibling;
end;

Alternatively, use an XPath query instead to focus on just the nodes you really want, eg:

uses
  ..., XmlIntf, XmlDom;

var
  ...
  XPath: IDOMNodeSelect;
  Nodes: IDOMNodeList;
  N: IDOMNode;
  I: Integer;

...

if Supports(Parent.DOMNode, IDOMNodeSelect, XPath) then
begin
  Nodes := XPath.selectNodes('KONTRAHENT[ID_KONTRAHENTA='+IntToStr(KontrNr)+']');
  for I := 0 to Nodes.length-1 do
  begin
    N := Nodes[i];
    // do something with N ...
  end;
end else
begin
  // code shown above ...
end;