I have created a possible solution by leveraging the standard configuration classes from .Net and some common patterns to achieve what you want.
Based on your sample XML and configuration I assume you want to perform on action (Mode) on element (Xpath) or an attribute (Attribute) on a given xml.
To make this possible I created an Interface that takes the element and the attribute to perform one single action. This can be implemented with a command pattern.
The correct implementation class is selected by using a factory pattern. By providing a Register method you enable users of your api to provide implementation of actions you didn't provide. You only share an common interface and the Factory class. The rest of the implementation is hidden.
Do notice that in this example there is only one instance of each command that is reused. If you require a per call instance the factory needs to be extended and would require concretetypes to be registered.
The main program would look this:
static void Main()
{
// get the config
var root = (Root) ConfigurationManager.GetSection("root");
// your input xml
var docRoot = XDocument.Parse(@"<parent><child1><superchild></superchild></child1><child2 Value=""3""></child2></parent>");
// loop over all actions that need to be performed
foreach (ModifyElement modact in root.ModifyActions)
{
var nodes = docRoot.XPathSelectElements(modact.Xpath);
// create the correct command class based on the value of Mode
var cmd = ModifyCommandFactory.GetInstance(modact.Mode);
foreach (var node in nodes)
{
// if we have an attribute, try to grab it
XAttribute attr = String.IsNullOrEmpty(modact.Attribute)?
null:node.Attributes(modact.Attribute).SingleOrDefault();
// now execute our command
cmd.Execute(node, attr);
}
}
var sb = new StringBuilder();
var sw = new XmlTextWriter(new StringWriter(sb));
docRoot.WriteTo(sw);
sw.Close();
Debug.WriteLine(sb);
}
The slightly adapted app.config file to hold your Modify commands
<configuration>
<configSections>
<section name="root" type="WindowsFormsApplication1.Root, WindowsFormsApplication1"/>
</configSections>
<root Value="">
<actions>
<Modify Xpath="/parent/child1/superchild" Attribute="" Mode="delete" />
<Modify Xpath="/parent/child2" Attribute="Value" Mode="multiply" />
</actions>
</root>
</configuration>
To read your config values a pretty basic implementation of the configuration classes is needed for the ConfigSection, ConfigCollection and ConfigElement. You find the code needed at the end of this post.
Interface
The common interface has an Execite method that takes an XElement and an XAttribute
public interface IModifyCommand
{
void Execute(XElement element, XAttribute attribute);
}
Commands
For each mode we implement a ConcreteCommand that implements IModifyCommand
Multiply
public class Multiply:IModifyCommand
{
public void Execute(XElement element, XAttribute attribute)
{
int value;
if (attribute != null &&
Int32.TryParse(attribute.Value, out value))
{
// this can break due to oveflow!
value *= value;
attribute.Value = value.ToString(CultureInfo.InvariantCulture);
}
if (element != null &&
Int32.TryParse(element.Value, out value))
{
// this can break due to oveflow!
value *= value;
element.Value = value.ToString(CultureInfo.InvariantCulture);
}
}
}
DeleteCommand
public class Delete:IModifyCommand
{
public void Execute(XElement element, XAttribute attribute)
{
if (attribute != null)
{
attribute.Remove();
}
if (element != null)
{
element.Remove();
}
}
}
NoopCommand
To make our life easier we also have a NoopCommand for the case when no suitable Mode is defined in the config.
public class Noop:IModifyCommand
{
public void Execute(XElement element, XAttribute attribute)
{
// do nothing
}
}
Factory to provide instances for several modes
public static class ModifyCommandFactory
{
static readonly Dictionary<string,IModifyCommand> commands = new Dictionary<string, IModifyCommand>();
static ModifyCommandFactory()
{
// our well known commands
Register("delete", new Delete());
Register("multiply", new Multiply());
}
internal static IModifyCommand GetInstance(string key)
{
IModifyCommand cmd;
if (!commands.TryGetValue(key, out cmd))
{
cmd = new Noop(); // nothing found, so use the No-operation command
}
return cmd;
}
// will be used by users to provide their own commands
public static void Register(string mode, IModifyCommand modifycommand)
{
commands.Add(mode, modifycommand);
}
}
ConfigSection
public class Root:ConfigurationSection
{
private const string ValueKey = "Value";
private const string ActionsKey = "actions";
[ConfigurationProperty(ValueKey,
IsRequired = true,
IsKey = false)]
public string Value
{
get { return (string)this[ValueKey];}
set { this[ValueKey] = value; }
}
[ConfigurationProperty(ActionsKey,
IsRequired = true,
IsKey = false)]
public ModifyCollection ModifyActions
{
get { return (ModifyCollection)this[ActionsKey]; }
set { this[ActionsKey] = value; }
}
}
ConfigCollection
public class ModifyCollection:ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ModifyElement();
}
public override ConfigurationElementCollectionType CollectionType
{
get{ return ConfigurationElementCollectionType.BasicMap;}
}
protected override object GetElementKey(ConfigurationElement element)
{
var me = (ModifyElement) element;
return me.Xpath + me.Attribute + me.Mode;
}
protected override string ElementName
{
get { return "Modify"; }
}
}
ConfigElement
public class ModifyElement:ConfigurationElement
{
private const string XPathKey = "Xpath";
private const string AttributeKey = "Attribute";
private const string ModeKey = "Mode";
[ConfigurationProperty(XPathKey,
IsRequired = true,
IsKey = false)]
public string Xpath
{
get { return (string)this[XPathKey]; }
set { this[XPathKey] = value; }
}
[ConfigurationProperty(AttributeKey,
IsRequired = true,
IsKey = false)]
public string Attribute
{
get { return (string)this[AttributeKey]; }
set { this[AttributeKey] = value; }
}
[ConfigurationProperty(ModeKey,
IsRequired = true,
IsKey = false)]
public string Mode
{
get { return (string)this[ModeKey]; }
set { this[ModeKey] = value; }
}
}