How can I use Nant's xmlpoke target to remove a node

后端 未结 2 451
失恋的感觉
失恋的感觉 2021-02-07 10:52

Given the following xml:


   Content A
   Content A
<         


        
相关标签:
2条回答
  • 2021-02-07 11:32

    I hold my hands up! This is a classic case of asking the wrong question.

    The problem is that you can't use xmlpoke to remove a single node. Xmlpoke can only be used to edit the contents of a specific node or attribute. There isn't an elegant way to remove a child node as per the question using only the standard Nant targets. It can be done using some inelegant string manipulation using properties in Nant, but why would you want to?

    The best way to do this is to write a simple Nant target. Here's one I prepared earlier:

    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 the above with a modification to the NAnt.exe.config file to load the custom target and the following script in the build file:

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

    This will remove the childnode with an argument arg with value b from target.xml. Which is what I actually wanted in the first place!

    0 讨论(0)
  • 2021-02-07 11:50

    xmlpeek only reads values. xmlpoke only sets values. Unfortunately, nant does not have an xmldelete task.

    I solved this issue by creating a nant <target /> in a nant file I can easily re-use between project.

    I chose to leverage nant's built-in <regex />, <echo />, <include /> and <call /> tasks.

    Advantages:

    • Works with regular expression (see disadvantages).
    • Can match any text, including XML!

    Disadvantages:

    • Have you ever solved a problem with a regex? If yes, you now you have another problem!
    • The regex value in the nant file MUST be escaped! (Use an online xml escaper tool).
    <!-- 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>
    

    Below is how to include and call this target. Keep in mind the all the parameters are global and must be defined before calling the target.

    • delete.from.file.path: the path of the file to be modified.
    • delete.from.file.regex: the regex matching what to removed (and defining BEFORE and AFTER groups).
    <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>
    
    
    0 讨论(0)
提交回复
热议问题