Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to programmatically add XmlNode to an XmlNodeList

Tags:

c#

.net

xml

I have an XmlNodeList of products whose values are put into a table. Now I want to add a new XmlNode to the list when a certain product is found so that in the same loop the new products is treated the same as the items that are originally in the file. This way the structire of the function does not need to change, just add an extra node that is processed next. But an XmlNode is an abstract class and I cant figure out how to create the new node programatically. Is this possible?

XmlNodeList list = productsXml.SelectNodes("/portfolio/products/product");

for (int i = 0; i < list.Count; i++)
{
  XmlNode node = list[i];

  if (node.Attributes["name"].InnertText.StartsWith("PB_"))
  {
    XmlNode newNode = ????
    list.InsertAfter(???, node);
  }

  insertIntoTable(node);
}
like image 309
Serve Laurijssen Avatar asked Sep 05 '25 03:09

Serve Laurijssen


1 Answers

XmlDocument is the factory for its nodes so you have to do this:

XmlNode newNode = document.CreateNode(XmlNodeType.Element, "product", "");

Or its shortcut:

XmlNode newNode = document.CreateElement("product");

Then to add newly created node to its parent:

node.ParentNode.AppendChild(newNode);

If added node must be processed then you have to do it explicitly: node list is a snapshot of nodes that matched search condition then it won't be dynamically updated. Simply call insertIntoTable() for added node:

insertIntoTable(node.ParentNode.AppendChild(newNode));

If your code is much different you may need little bit of refactoring to make this process a two step batch (first search for nodes to add and then process them all). Of course you may even follow a completely different approach (for example copying nodes from XmlNodeList to a List and then adding them to both lists).

Assuming this is enough (and you do not need refactoring) let's put everything together:

foreach (var node in productsXml.SelectNodes("/portfolio/products/product"))
{
  if (node.Attributes["name"].InnertText.StartsWith("PB_"))
  {
    XmlNode newNode = document.CreateElement("product");
    insertIntoTable(node.ParentNode.AppendChild(newNode));
  }

  // Move this before previous IF in case it must be processed
  // before added node
  insertIntoTable(node);
}

Refactoring

Refactoring time (and if you have a 200 lines function you really need it, much more than what I present here). First approach even if not very efficient:

var list = productsXml
    .SelectNodes("/portfolio/products/product")
    .Cast<XmlNode>();
    .Where(x.Attributes["name"].InnertText.StartsWith("PB_"));

foreach (var node in list)
    node.ParentNode.AppendChild(document.CreateElement("product"));

foreach (var node in productsXml.SelectNodes("/portfolio/products/product"))
    insertIntoTable(node); // Or your real code

If you do not like a two passes approach you may use ToList() like this:

var list = productsXml
    .SelectNodes("/portfolio/products/product")
    .Cast<XmlNode>()
    .ToList();

for (int i=0; i < list.Count; ++i)
{
    var node = list[i];

    if (node.Attributes["name"].InnertText.StartsWith("PB_"))
      list.Add(node.ParentNode.AppendChild(document.CreateElement("product"))));

    insertIntoTable(node);
}

Please note that in second example the use of for instead of foreach is mandatory because you change the collection within the loop. Note that you may even keep your original XmlNodeList object in place...

like image 53
Adriano Repetti Avatar answered Sep 07 '25 20:09

Adriano Repetti