We help IT Professionals succeed at work.

Delphi & XML: Inserting a node to the in-memory IXMLDOMNodeList

rfwoolf
rfwoolf asked
on
Hi all

Let's say I load an XML file, I then extract some nodes into a IXMLDOMNodeList and now I want to add a node somewhere in the middle of the IXMLDOMNodeList?

Here's an example:

Let's say I have an XML file like this:
<xml>
  <LoopItem file="Casablanca.mpg" />
  <LoopItem file="StarWars.mpg" />
  <LoopItem file="PanicRoom.mpg" />
</xml>

and some code like this:

uses
  msxml;

var
  LoopNodes : IXMLDOMNodeList;

begin
  XML := CoDOMDocument.create;
  xml.async := false; //What the hell does this do?

  XML.Load('C:\MyStupidXMLFile.xml');

  LoopNodes := XML.getElementsByTagName('LoopItem');
end;

This is the pseudocode of what I want to do:
  LoopNodes.InsertNode(2, '<LoopItem file="MinorityReport.mpg" />');
  //Insert into node position 2 the node '<LoopItem file="MinorityReport.mpg" />'
Comment
Watch Question

Top Expert 2010

Commented:
Hi rfwoolf. Try this code (I've commented all operations):

procedure TForm1.Button1Click(Sender: TObject);
var
  XML: IXMLDOMDocument;
  LoopNodes : IXMLDOMNodeList;
  RootNode, NewNode, NewAttrib: IXMLDOMNode;
begin
  XML := CoDOMDocument.create;

  xml.async:= false; // Specifies if asynchronous download is permitted. Should be false in this case

  XML.Load('C:\MyStupidXMLFile.xml');

  RootNode:= XML.documentElement;

  NewNode:= XML.CreateNode(1, 'LoopItem', ''); // creating new node
  NewAttrib:= XML.createAttribute( 'file' );   // creating attribute
  NewAttrib.nodeValue:= 'MinorityReport.mpg';  // initializing attribute
  NewNode.attributes.setNamedItem( NewAttrib ); //  adding attribute to new node

  RootNode.insertBefore( NewNode, RootNode.ChildNodes.Item[2] ); // adding new node to xml

  Memo1.Lines.Text:= RootNode.xml; // view result xml in memo

  XML.save( 'C:\newxml.xml' ); // and saving it to new file
end;

Author

Commented:
Wow thanks so much for your answer, I'm yet to try it but it looks good - I couldn't figure out how to use insertbefore, and the ADSL is down so I had to post on ee using my phone as a modem.

Author

Commented:
Hmmm I'm beginning to try it but it doesn't look like uve used Loopnodes, but ill try to adapt.
Top Expert 2010

Commented:
I've checked it with xml example that you gave in the question. If you have more complex xml, for example like this:

<xml>
  <LoopNodes>
    <LoopItem file="Casablanca.mpg" />
    <LoopItem file="StarWars.mpg" />
    <LoopItem file="PanicRoom.mpg" />
  </LoopNodes>
</xml>

change one line in my function, like this:

  // get LoopNodes node instead of root node
  // RootNode:= XML.documentElement;
  RootNode:= XML.getElementsByTagName('LoopNodes').item[0];

Hope this help

Author

Commented:
Thanks,
The problem is that loopnodes as I understand it only contains nodes with name "loopitem" and thus when u use ur insertbefore statement, the position does not take it into account, so if our xml has some nodes called scheduleitem intermixed among the nodes called loopitem, and u say position 2, does it know I mean loopitem 2 or will it mean any node in 2nd position.
Top Expert 2010

Commented:
do you mean the xml like this:


<xml>
  <LoopNodes>
     <LoopItem file="Casablanca.mpg" />
     <scheduleitem />
     <LoopItem file="StarWars.mpg" />
     <scheduleitem />

      ->you want to add new item here

     <LoopItem file="PanicRoom.mpg" />
   </LoopNodes>
</xml>

Author

Commented:
Basically the xml files are dynamically generated and therefore they may have just 1 loopitem or many, they may have 0 scheduleitems or many and they may be intermixed. The software knows what loopitem it is busy with, say 3, and the software uses the loopnodes variable of type IXMLDOMNodes which as u can see Is assigned by  GetElementsByTagName('LoopItem'). So I want to say insert another Loopitem after Loopnode.items[3]. All I would know is the number of Loopnode, and therefore the number of Loopitem sequencually, but I won't know which number literal node in the xml itself. I hope this makes sense
Top Expert 2010
Commented:
ok. try this:

procedure TForm1.Button1Click(Sender: TObject);
var
  XML: IXMLDOMDocument;
  LoopNodes : IXMLDOMNodeList;
  ParentNode, NewNode, NewAttrib: IXMLDOMNode;
begin
  XML := CoDOMDocument.create;

  xml.async:= false; // Specifies if asynchronous download is permitted. Should be false in this case

  XML.Load('MyStupidXMLFile.xml');

  // we even don't know the parent name of loopitem
  ParentNode:= XML.getElementsByTagName('LoopItem').item[0].parentNode;

  NewNode:= XML.CreateNode(1, 'LoopItem', ''); // creating new node
  NewAttrib:= XML.createAttribute( 'file' );   // creating attribute
  NewAttrib.nodeValue:= 'MinorityReport.mpg';  // initializing attribute
  NewNode.attributes.setNamedItem( NewAttrib ); //  adding attribute to new node

  LoopNodes:=XML.getElementsByTagName('LoopItem');
  ParentNode.insertBefore( NewNode, LoopNodes[2] ); // adding new node before the 3rd loopitem

//  Memo1.Lines.Text:= XML.documentElement.xml; // view result xml in memo

  XML.save( 'newxml.xml' ); // and saving it to new file
end;

Author

Commented:
Okay I think I have a way that adapts ur solution. I create a brand new xml doc and say loadxml('').
Then I use that as my rootnode, I import all the loopnodes items into it using insertbefore which will then give me an xml doc where the structure and order of the nodes *will* be the same as loopnodes, and therefore I can insert into whatever position.
Then I assign loopnodes to this new document. A bit of a mission and maybe there's a better way?
In any case this has been very useful. Thanks!
Top Expert 2010

Commented:
did you check my last function?

Author

Commented:
Thanks I did try ur last function.and it works great, and in fact I'm going to have to take a moment to understand why it works.
Thanks so much, this was really holding me back.
FYI I still had to reassign loopnodes to the xml.getelementsbytagmame('loopitem).

Author

Commented:
A great solution