Helper class for Adding XML to documents using the Rules Engine

With the rules engine you can't directly add a node to an XML document in a rules action. On the other time you often would like to do that. No worries, you can call a helper class to do the work for you. Here is an example helper class from John Rummell <welcome back> that does the trick. Nothing particularly revolutionary in the code but it will save you some time I'm sure.

using System;

using System.Xml;

using Microsoft.RuleEngine;

namespace XMLHelpers

{

      /// <summary>

      /// Class used to add nodes and/or attributes to a TypedXmlDocument

      ///

      /// Using the example:

      /// <root>

      /// <a>

      /// </a>

      /// </root>

      ///

      /// AddNode(doc, "/root/a", "b") or AddNodeIfNotThere(doc, "/root/a", "b") will result in:

      /// <root>

      /// <a>

      /// <b />

      /// </a>

      /// </root>

      ///

      /// AddAttribute(doc, "//a", "name", "value") will result in:

      /// <root>

      /// <a name="value">

      /// </a>

      /// </root>

      ///

      /// The code does not create intermediate nodes (e.g. can't create a "c" inside "b"

      /// if "b" doesn't exist. As a result, you need to sequence the calls:

      /// AddNode(doc, "/root/a", "b");

      /// AddNode(doc, "/root/a/b", "c");

      /// Note that if "b" already exists, a second "b" node will be created. Use AddNodeIfNotThere

      /// to create the node "b" if it doesn't already exist, but leave it alone if it is.

      /// </summary>

      public class XmlCreateNodes

      {

            /// <summary>

            /// Add an attribute with a specified name and value into the node selected by the XPath

            /// </summary>

            /// <param name="txd">RuleEngine wrapper for an XmlDocument (or some part of it)</param>

            /// <param name="xPath">XPath that must select a node (null or "" to use current node)</param>

            /// <param name="attributeName">Attribute name</param>

            /// <param name="attributeValue">Attribute value</param>

           public void AddAttribute(TypedXmlDocument txd, string xPath, string attributeName, string attributeValue)

            {

                  // can we find the XPath indicated?

                  // we need an XmlElement in order to set the attribute, not the usual XmlNode

                  // if the result is not an XmlElement, we don't work

                  XmlElement node = LocateXPath(txd, xPath) as XmlElement;

                  // if we found a matching node, add the attribute

                  if (null != node)

                  {

                        node.SetAttribute(attributeName, attributeValue);

                  }

            }

            /// <summary>

            /// Add an new node with a specified name and namespace into the node selected by the XPath

            /// </summary>

            /// <param name="txd">RuleEngine wrapper for an XmlDocument (or some part of it)</param>

            /// <param name="xPath">XPath that must select a node (null or "" to use current node)</param>

            /// <param name="nodeName">Name for the new node</param>

            /// <param name="nodeNamespace">Namespace for the new node</param>

            public void AddNode(TypedXmlDocument txd, string xPath, string nodeName, string nodeNamespace)

            {

                  // can we find the XPath indicated?

                  XmlNode node = LocateXPath(txd, xPath);

                  if (null == node) return;

                  // determine the root node for the document

                  // if the XPath selects the TXD, it will have no root if it is an XmlDocument

                  // in that case, simply use the document from the TXD

                  XmlDocument root = node.OwnerDocument;

                  if (null == root)

                  {

                        // if the XPath selects the TXD, it may

                        // so fix accordingly

                        root = txd.Document as XmlDocument;

                        if (null == root) return;

                  }

                  // create a new node and add it in

                  XmlElement newNode = root.CreateElement(nodeName, nodeNamespace);

                  node.AppendChild(newNode);

            }

            /// <summary>

            /// Add an new node with a specified name into the node selected by the XPath

            /// </summary>

            /// <param name="txd">RuleEngine wrapper for an XmlDocument (or some part of it)</param>

            /// <param name="xPath">XPath that must select a node (null or "" to use current node)</param>

            /// <param name="nodeName">Name for the new node</param>

            public void AddNode(TypedXmlDocument txd, string xPath, string nodeName)

            {

                  // can we find the XPath indicated?

                  XmlNode node = LocateXPath(txd, xPath);

                  if (null == node) return;

                  // determine the root node for the document

                  // if the XPath selects the TXD, it will have no root if it is an XmlDocument

                  // in that case, simply use the document from the TXD

                  XmlDocument root = node.OwnerDocument;

                  if (null == root)

                  {

                        // if the XPath selects the TXD, it may

                        // so fix accordingly

                        root = txd.Document as XmlDocument;

                        if (null == root) return;

                  }

                  // create a new node and add it in

                  XmlElement newNode = root.CreateElement(nodeName);

                  node.AppendChild(newNode);

            }

            /// <summary>

            /// Add an new node with a specified name and namespace into the node selected by the XPath

            /// provided that it doesn't already exist

            /// </summary>

            /// <param name="txd">RuleEngine wrapper for an XmlDocument (or some part of it)</param>

            /// <param name="xPath">XPath that must select a node (null or "" to use current node)</param>

            /// <param name="nodeName">Name for the new node</param>

            /// <param name="nodeNamespace">Namespace for the new node</param>

           public void AddNodeIfNotThere(TypedXmlDocument txd, string xPath, string nodeName, string nodeNamespace)

            {

                  // can we find the XPath indicated?

                  XmlNode node = LocateXPath(txd, xPath);

                  if (null == node) return;

                  // does the new element already exist?

                  foreach (XmlNode child in node.ChildNodes)

                  {

                        if ((child.LocalName == nodeName) && (child.NamespaceURI == nodeNamespace)) return;

                  }

                  // determine the root node for the document

                  // if the XPath selects the TXD, it will have no root if it is an XmlDocument

                  // in that case, simply use the document from the TXD

                  XmlDocument root = node.OwnerDocument;

                  if (null == root)

                  {

                        // if the XPath selects the TXD, it may

                        // so fix accordingly

                        root = txd.Document as XmlDocument;

                        if (null == root) return;

                  }

                  // create a new node and add it in

                  XmlElement newNode = root.CreateElement(nodeName, nodeNamespace);

                  node.AppendChild(newNode);

            }

            /// <summary>

            /// Add an new node with a specified name into the node selected by the XPath

            /// provided that it doesn't already exist

            /// </summary>

            /// <param name="txd">RuleEngine wrapper for an XmlDocument (or some part of it)</param>

            /// <param name="xPath">XPath that must select a node (null or "" to use current node)</param>

            /// <param name="nodeName">Name for the new node</param>

           public void AddNodeIfNotThere(TypedXmlDocument txd, string xPath, string nodeName)

            {

                  // can we find the XPath indicated?

                  XmlNode node = LocateXPath(txd, xPath);

                  if (null == node) return;

                  // does the new element already exist?

                  foreach (XmlNode child in node.ChildNodes)

                  {

                        if (child.LocalName == nodeName) return;

                  }

                  // determine the root node for the document

                  // if the XPath selects the TXD, it will have no root if it is an XmlDocument

                  // in that case, simply use the document from the TXD

                  XmlDocument root = node.OwnerDocument;

                  if (null == root)

                  {

                        // if the XPath selects the TXD, it may

                        // so fix accordingly

                        root = txd.Document as XmlDocument;

                        if (null == root) return;

                  }

                  // create a new node and add it in

                  XmlElement newNode = root.CreateElement(nodeName);

                  node.AppendChild(newNode);

            }

            /// <summary>

            /// Select the first node that the specified XPath points to inside the XmlDocument

            /// </summary>

            /// <param name="txd">RuleEngine wrapper for an XmlDocument (or some part of it)</param>

            /// <param name="xPath">XPath that must select a node (null or "" to use current node)</param>

            /// <returns></returns>

            internal XmlNode LocateXPath(TypedXmlDocument txd, string xPath)

            {

                  // does the TXD contain a node?

                  XmlNode parent = txd.Document;

                  if (null == parent) return null;

                  // is there an XPath specified? if not, return the parent

                  if ((xPath == null) || (xPath == string.Empty)) return parent;

                 

                  // return the first node that the XPath points to

                  return parent.SelectSingleNode(xPath, txd.NamespaceManager);

            }

      }

}