如何使用 Nant 的 xmlpoke 目标删除节点

发布于 2024-07-13 22:08:57 字数 759 浏览 8 评论 0原文

给定以下 xml:

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

使用 XMLPoke 和以下 XPath :

rootnode/childnode[arg='b']

结果(如果替换字符串为空)是:

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

当我们真正想要删除子节点本身时,子节点的内容已被删除。 期望的结果是:

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

必须根据 childnode 参数选择子节点。

Given the following xml:

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

Using XMLPoke with the following XPath:

rootnode/childnode[arg='b']

The result (if the replace string is empty) is:

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

The contents of the childnode have been removed when we actually want the childnode itself removed. The desired result is:

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

The childnode must be selected based on the childnode argument.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浮云落日 2024-07-20 22:08:57

我举起双手! 这是一个典型的问错问题的案例。

问题是您无法使用 xmlpoke 删除单个节点。 Xmlpoke只能用于编辑特定节点或属性的内容。 没有一种优雅的方法可以根据问题仅使用标准 Nant 目标来删除子节点。 可以通过使用 Nant 中的属性进行一些不优雅的字符串操作来完成,但是为什么你想要这样做呢?

做到这一点的最佳方法是编写一个简单的 Nant 目标。 这是我之前准备的:

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

将上述内容与对 NAnt.exe.config 文件的修改相结合,以加载自定义目标以及 build 文件中的以下脚本:

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

这将从 target.xml 中删除带有值为 b 的参数 argchildnode。 这才是我最初真正想要的!

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!

我纯我任性 2024-07-20 22:08:57

xmlpeek 仅读取值。
xmlpoke 仅设置值。
不幸的是,nant 没有 xmldelete 任务。

我通过在 nant 文件中创建一个 nant 解决了这个问题,我可以轻松地在项目之间重复使用。

我选择利用 nant 的内置 任务。

优点:

  • 适用于正则表达式(参见缺点)。
  • 可以匹配任何文本,包括XML!

缺点:

  • 您曾经使用正则表达式解决过问题吗? 如果是的话,那么你现在还有另一个问题!
  • nant 文件中的正则表达式值必须被转义! (使用在线 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]*)\<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>

以下是如何包含和调用该目标。 请记住,所有参数都是全局的,并且必须在调用目标之前定义。

  • delete.from.file.path:要修改的文件的路径。
  • delete.from.file.regex:与要删除的内容匹配的正则表达式(并定义 BEFORE 和 AFTER 组)。
<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>

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]*)\<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>

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="(?'BEFORE'[\w\s\W]*)\<childnode.*arg="b">(.*?)\<\/childnode\>(?'AFTER'[\w\s\W]*)" />
        <call target="RemoveLineFromFile" />

    </target>

</project>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文