Ho creato una possibile soluzione sfruttando le classi di configurazione standard di .Net e alcuni modelli comuni per ottenere ciò che desideri.
Sulla base del tuo esempio XML e configurazione presumo che tu voglia eseguire l'azione (Modalità) sull'elemento (Xpath) o un attributo (Attributo) su un dato xml.
Per rendere ciò possibile ho creato un'interfaccia che accetta l'elemento e l'attributo per eseguire una singola azione. Questo può essere implementato con un schema di comando .
La classe di implementazione corretta viene selezionata utilizzando un modello di fabbrica . Fornendo un metodo di registrazione si consente agli utenti della propria API di fornire l'implementazione delle azioni che non sono state fornite. Condividete solo un'interfaccia comune e la classe Factory. Il resto dell'implementazione è nascosto.
Si noti che in questo esempio c'è solo un'istanza di ciascun comando che viene riutilizzata. Se hai bisogno di un'istanza per chiamata, la fabbrica deve essere estesa e richiederebbe la registrazione di tipi concreti.
Il programma principale avrebbe questo aspetto:
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);
}
Il file app.config leggermente adattato per contenere i comandi Modifica
<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>
Per leggere i valori di configurazione è necessaria un'implementazione piuttosto semplice delle classi di configurazione per ConfigSection, ConfigCollection e ConfigElement. Troverai il codice necessario alla fine di questo post.
Interfaccia
L'interfaccia comune ha un metodo Execite che accetta XElement e XAttribute
public interface IModifyCommand
{
void Execute(XElement element, XAttribute attribute);
}
Comandi
Per ogni modalità implementiamo un ConcreteCommand che implementa 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
Per semplificarci la vita, abbiamo anche un NoopCommand per il caso in cui non è definita una modalità adeguata nella configurazione.
public class Noop:IModifyCommand
{
public void Execute(XElement element, XAttribute attribute)
{
// do nothing
}
}
Fabbrica per fornire istanze per diverse modalità
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; }
}
}