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);
}
}
}