Domanda

Dato il seguente codice XML:

<rootnode>
   <childnode arg="a">Content A</childnode>
   <childnode arg="b">Content A</childnode>
</rootnode>

XMLPoke con il seguente XPath:

rootnode/childnode[arg='b']

Il risultato (se la stringa è vuota sostituire) è:

<rootnode>
   <childnode arg="a">Content A</childnode>
   <childnode arg="b"></childnode>
</rootnode>

Il contenuto della childnode sono stati rimossi quando vogliamo davvero il childnode sé rimosso. Il risultato desiderato è:

<rootnode>
   <childnode arg="a">Content A</childnode>
</rootnode>

Il childnode deve essere selezionato in base all'argomento childnode.

È stato utile?

Soluzione

tengo le mie mani! Questo è un classico caso di porre la domanda sbagliata.

Il problema è che non è possibile utilizzare xmlpoke per rimuovere un singolo nodo. Xmlpoke può essere utilizzato solo per modificare il contenuto di un nodo o attributo specifico. Non c'è un modo elegante per rimuovere un nodo figlio come per la domanda utilizzando solo lo standard Nant bersagli. E ' possono essere fatto utilizzando un po' di manipolazione di stringhe poco elegante utilizzando immobili a Nant, ma perché si vorrebbe?

Il modo migliore per farlo è quello di scrivere un semplice obiettivo di Nant. Ecco quello che ho preparato in precedenza:

using System;
using System.IO;
using System.Xml;
using NAnt.Core;
using NAnt.Core.Attributes;

namespace XmlStrip
{
    [TaskName("xmlstrip")]
    public class XmlStrip : Task
    {
        [TaskAttribute("xpath", Required = true), StringValidator(AllowEmpty = false)]
        public string XPath { get; set; }

        [TaskAttribute("file", Required = true)]
        public FileInfo XmlFile { get; set; }

        protected override void ExecuteTask()
        {
            string filename = XmlFile.FullName;
            Log(Level.Info, "Attempting to load XML document in file '{0}'.", filename );
            XmlDocument document = new XmlDocument();
            document.Load(filename);
            Log(Level.Info, "XML document in file '{0}' loaded successfully.", filename );

            XmlNode node = document.SelectSingleNode(XPath);
            if(null == node)
            {
                throw new BuildException(String.Format("Node not found by XPath '{0}'", XPath));
            }

            node.ParentNode.RemoveChild(node);

            Log(Level.Info, "Attempting to save XML document to '{0}'.", filename );
            document.Save(filename);
            Log(Level.Info, "XML document successfully saved to '{0}'.", filename );
        }
    }
}

Unire il sopra con una modifica al NAnt.exe.config file per caricare il target personalizzato e il seguente script in costruire file:

<xmlstrip xpath="//rootnode/childnode[@arg = 'b']" file="target.xml" />

Ciò eliminerà la childnode con un argomento arg con il valore di b da target.xml . Che è quello che in realtà volevo in primo luogo!

Altri suggerimenti

xmlpeek legge solo i valori. xmlpoke solo imposta valori. Purtroppo, Nant non ha un xmldelete compito.

Ho risolto questo problema creando un <target /> Nant in un file di Nant posso facilmente riutilizzo tra progetto.

Ho scelto di sfruttare le attività <regex /> built-in, <echo />, <include /> e <call /> di Nant.

Vantaggi:

  • Funziona con espressioni regolari (vedi svantaggi).
  • Può abbinare qualsiasi testo, tra cui XML!

Svantaggi:

  • Hai mai risolto un problema con una regex? Se sì, è ora di avere un altro problema!
  • Il valore regex nel file Nant deve essere sfuggito! (Utilizzare uno strumento di escape XML on-line).
<!-- This file should be included before being called -->
<project name="YourProject">

    <target name="RemoveLineFromFile">

        <loadfile 
            file="${delete.from.file.path}" 
            property="xml.file.content" />
        <property 
            name="delete.from.file.regex" 
            value="(?'BEFORE'[\w\s\W]*)\&lt;add.*key=\&quot;AWSProfileName\&quot;.*value=\&quot;comsec\&quot;.*\/\&gt;(?'AFTER'[\w\s\W]*)" />
        <regex 
          input="${xml.file.content}" 
          pattern="${delete.from.file.regex}" />
        <echo 
          file="${delete.from.file.path}"
          message="${BEFORE}${AFTER}"
          append="false" />

    </target>

</project>

Di seguito è come includere e chiamare questo obiettivo. Tenete a mente i tutti i parametri sono globali e deve essere definito prima chiamando il bersaglio.

  • delete.from.file.path:. Il percorso del file da modificare
  • delete.from.file.regex:. L'abbinamento regex cosa rimosso (e la definizione PRIMA e DOPO gruppi)
<project name="YourProject">

    <include buildfile="path\to\nant\target\RemoveLineFromFile.build"/>

    <target name="OtherWork">

        <property name="delete.from.file.path" value="path\to\xml\file.xml" />
        <property name="delete.from.file.regex" value="(?&apos;BEFORE&apos;[\w\s\W]*)\&lt;childnode.*arg=&quot;b&quot;&gt;(.*?)\&lt;\/childnode\&gt;(?&apos;AFTER&apos;[\w\s\W]*)" />
        <call target="RemoveLineFromFile" />

    </target>

</project>

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top