Esecuzione delle impostazioni di configurazione dell'utente

0

Per un'API sto costruendo ciò che fa è caricare un file XML che viene passato ad esso come una stringa, eseguire il drill sull'elemento corretto, modificare i valori e restituire l'XML modificato.

Sto facendo qualcosa di simile nel mio codice

if (valid)
{
    XDocument docRoot = XDocument.Parse(XMLstring);

    var node1 = docRoot.Descendants(namespace + @"node1");

    foreach (var result in node1) 
    {
        var child1 = result.Descendants(ns + "child1");

        var test = child1.First();
        test.SetValue(Transform(test.Value));
    }


    //return the transformed XML

}
else { }

Ora voglio creare un file di configurazione in formato XML che permetta all'utente di specificare quali nodi nel file XML modificare e quale modifica eseguire, (più avanti voglio utilizzarlo in una GUI)

quindi qualcosa di simile

<root Value="">
    <Modify Xpath="\parent\child1\superchild\" Attribute="" Mode="delete" />
    <Modify Xpath="\parent\child2\" Attribute="Value" Mode="multiply" />
</root>

E ho questi due pezzi ma non so come collegarli insieme. Penso di dover creare una classe dal mio file di configurazione XML e in qualche modo mappare quelli ai valori nell'altro XML.

    
posta erotavlas 11.04.2014 - 22:28
fonte

1 risposta

1

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; }
    }
}
    
risposta data 17.05.2014 - 19:40
fonte

Leggi altre domande sui tag