Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

delphi xmlchildnode gets attribute from parent

Tags:

xml

delphi

I am trying to write an xML in Delphi.

If I gife a node a xmlns attribute, the child node of that node shows the attribute also but then empty. How can I prefent that the child node shows the attribute?

I tested with the following code

procedure TForm2.Button1Click(Sender: TObject);
var
  RootNode, CurNode, PmtNode, PmtDetNode : IXMLNODE;
  I:Integer;
begin
   SepaDoc := Newxmldocument;
   SepaDoc.Encoding := 'utf-8';
   SepaDoc.Options := [doNodeAutoIndent];
   RootNode := SepaDoc.AddChild('Document');
   CurNode := RootNode.AddChild('Child1');
   CurNode.Attributes['xmlns'] := 'apenootje';
   CurNode := CurNode.AddChild('Child2');
   CurNode := CurNode.AddChild('Child3');
   SepaDoc.SaveToFile('D:\indir\testsepa.xml');
end;

This result in the following XML

<?xml version="1.0" encoding="UTF-8"?>
-<Document>   -<Child1 xmlns="apenootje">
    -<Child2 xmlns="">
       <Child3/>
     </Child2>
    </Child1>    
  </Document>

Thanks Rob Nowee

like image 445
user3074489 Avatar asked Feb 14 '23 11:02

user3074489


2 Answers

Since the child elements of Child1 don't carry the same namespace, it must be undeclared and that's the reason that Child2 is holding the empty (default) namespace.

This is known as Namespace undeclaration

When an element carries the attribute xmlns="", the default namespace for that element and its descendants reverts to "no namespace": that is, unprefixed names are considered not to be in any namespace.

XML Namespaces 1.1 also introduces the option to undeclare other namespace prefixes. For example, if the attribute xmlns:p="" appears on an element, the namespace prefix p is no longer in scope (and therefore cannot be used) on that element or on its descendants, unless reintroduced by another namespace declaration

That being said, the fix is simple; include the namespace on all subsequent child nodes:

program SO20424534;

{$APPTYPE CONSOLE}

uses
  ActiveX,
  XMLdom,
  XMLDoc,
  XMLIntf,
  SysUtils;

function TestXML : String;

var
  RootNode,
  CurNode    : IXMLNODE;
  Doc        : IXmlDocument;
  ns         : String;

begin
 Doc := Newxmldocument;
 ns := 'apenootje';
 Doc.Encoding := 'utf-8';
 Doc.Options := [doNodeAutoIndent];
 RootNode := Doc.AddChild('Document');
 CurNode := RootNode.AddChild('Child1');
 CurNode.DeclareNamespace('', ns);
 CurNode := CurNode.AddChild('Child2', ns);
 CurNode := CurNode.AddChild('Child3', ns);
 Result := Doc.XML.Text;
end;
    
begin
  try
   CoInitialize(nil);
   try 
    Writeln(TestXML);
   finally
    CoUninitialize;
   end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
 Readln;
end;

Output:

<?xml version="1.0"?>
<Document>
  <Child1 xmlns="apenootje">
    <Child2>
      <Child3/>
    </Child2>
  </Child1>
</Document>
like image 62
whosrdaddy Avatar answered Feb 23 '23 16:02

whosrdaddy


You have to declare a namespace before you can use it. It is not enough to simply add an xmlns attribute manually, which is not the correct way to let the DOM know about the existence of the namespace. Use IXMLNode.DeclareNamespace() for that instead, eg:

CurNode := RootNode.AddChild('Child1');
CurNode.DeclareNamespace('', 'apenootje');
like image 40
Remy Lebeau Avatar answered Feb 23 '23 17:02

Remy Lebeau