Come posso usare bersaglio xmlpoke di Nant per rimuovere un nodo
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.
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]*)\<add.*key=\"AWSProfileName\".*value=\"comsec\".*\/\>(?'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="(?'BEFORE'[\w\s\W]*)\<childnode.*arg="b">(.*?)\<\/childnode\>(?'AFTER'[\w\s\W]*)" />
<call target="RemoveLineFromFile" />
</target>
</project>