Pergunta

Dado o seguinte xml:

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

Usando XMLPoke com o seguinte XPath:

rootnode/childnode[arg='b']

O resultado (se o texto de substituição está vazia) é:

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

O conteúdo do childNode foram removidos quando nós realmente deseja que o childNode si removido. O resultado desejado é:

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

O childNode devem ser selecionados com base no argumento childNode.

Foi útil?

Solução

Eu mantenho minhas mãos para cima! Este é um caso clássico de fazer a pergunta errada.

O problema é que você não pode usar xmlpoke para remover um único nó. Xmlpoke só pode ser usado para editar o conteúdo de um nó ou atributo específico. Não é uma maneira elegante para remover um nó filho como pela questão usando apenas o padrão Nant alvos. É pode ser feito usando alguma manipulação corda deselegante usando propriedades em Nant, mas por que você iria querer?

A melhor maneira de fazer isso é escrever um simples alvo Nant. Aqui está um que eu preparado anteriormente:

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 );
        }
    }
}

Combine acima com uma modificação para o NAnt.exe.config arquivo para carregar o alvo personalizado e o seguinte script no construção arquivo:

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

Isto irá remover o childNode com um argumento arg com valor b de target.xml . Que é o que eu realmente queria, em primeiro lugar!

Outras dicas

xmlpeek só lê valores. xmlpoke só estabelece valores. Infelizmente, nant não tem um xmldelete tarefa.

Eu resolvi esse problema criando um <target /> nant em um arquivo nant posso facilmente re-uso entre o projeto.

Eu escolhi a alavancagem da nant built-in tarefas <regex />, <echo />, <include /> e <call />.

Vantagens:

  • Funciona com expressão regular (ver desvantagens).
  • Pode combinar com qualquer texto, incluindo XML!

Desvantagens:

  • Você já resolveu um problema com um regex? Se sim, você agora você tem um outro problema!
  • O valor regex no arquivo nant deve ser escapado! (Use uma ferramenta online escaper xml).
<!-- 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>

A seguir é como incluir e chamar esta meta. Tenha em mente os todos os parâmetros são globais e devem ser definidos antes de chamar o alvo.

  • delete.from.file.path:. O caminho do arquivo a ser modificado
  • delete.from.file.regex:. A correspondência regex o que removeu (e definir antes e depois de grupos)
<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>

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top