如何使用C#替换特定的xml节点元素

4

给定本地的xml文件:

<products>
  <product id="p1">
    <name>Delta</name>
    <price>800</price>
    <stock>4</stock>
    <country>Denmark</country>
  </product>
  <product id="p2">
    <name>Golf</name>
    <price>1000</price>
    <stock>5</stock>
    <country>Germany</country>
  </product>
  <product id="p3">
    <name>Alfa</name>
    <price>1200</price>
    <stock>19</stock>
    <country>Germany</country>
  </product>
  <product id="p4">
    <name>Foxtrot</name>
    <price>1500</price>
    <stock>5</stock>
    <country>Australia</country>
  </product>
  <product id="p5">
    <name>Tango</name>
    <price>1225</price>
    <stock>3</stock>
    <country>Japan</country>
  </product>
</products>

我尝试替换产品节点“p1”中的价格元素如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System;
using System.Xml.XPath;
using System.Xml.Linq;

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(@"products.xml");

Console.WriteLine("\n\nDisplay the initial XML...");
xmlDoc.Save(Console.Out);

//Create an XmlNamespaceManager for resolving namespaces.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("products", "product");

// replace the node with a new one
//Select the profile node with the matching attribute value.
XmlNode product;
XmlElement root = xmlDoc.DocumentElement;
product = root.SelectSingleNode("descendant::product[id='p1']", nsmgr);

//Create a new price element.
XmlElement oldElem = xmlDoc.CreateElement("price");
oldElem.InnerText = "800";

//Create a new price element.
XmlElement newElem = xmlDoc.CreateElement("price");
newElem.InnerText = "125";

//Replace the price element.
root.ReplaceChild(newElem, root.FirstChild);
Console.WriteLine("\n\nDisplay the modified XML...");
xmlDoc.Save(Console.Out);

// save the document with the revised node
xmlDoc.Save(@"products2.xml");

问题在于新节点(价格)元素仅添加到产品p1节点中,当保存到磁盘时,会丢失p1的所有内容。我做错了什么?


当你说“替换”元素时,你是想要更新值(innertext)从800到125吗? - Andy Arndt
请创建一个 [mcve] 并删除所有不必要的代码以重现问题。 - Thomas Weller
你能提供你所期望的输出吗? - Wamadahama
3个回答

2
主要问题在于:
product = root.SelectSingleNode("descendant::product[id='p1']", nsmgr);

因为您未在以下使用变量。

下一个问题出现在[id='p1']中,因为您像访问元素一样访问ID,但应该是属性。改用[@id='p1']

其他事项:

  • 一个更好的方法可能是直接更新<price>节点的内部文本,而不是替换整个元素。
  • 在您的示例中不存在命名空间,因此不需要名称空间管理器。
  • 无需创建节点oldNode,该节点已经存在。
  • newElem未被使用。

建议修复您的代码:

using System;
using System.Xml;

namespace XmlUpdateNode
{
    class Program
    {
        static void Main()
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(@"products.xml");

            Console.WriteLine("\n\nDisplay the initial XML...");
            xmlDoc.Save(Console.Out);

            // replace the node with a new one
            //Select the profile node with the matching attribute value.
            var product = xmlDoc.SelectSingleNode("descendant::product[@id='p1']");

            //Create a new price element.
            XmlElement elem = xmlDoc.CreateElement("price");
            elem.InnerText = "125";

            //Replace the price element.
            product.ReplaceChild(elem, product.FirstChild.NextSibling);
            Console.WriteLine("\n\nDisplay the modified XML...");
            xmlDoc.Save(Console.Out);

            // save the document with the revised node
            xmlDoc.Save(@"products2.xml");
        }
    }
}

直接替换价格元素中的文本即可缩短代码,示例如下:
using System;
using System.Xml;

namespace XmlUpdateNode
{
    class Program
    {
        static void Main()
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(@"products.xml");

            Console.WriteLine("\n\nDisplay the initial XML...");
            xmlDoc.Save(Console.Out);

            // replace the price directly
            var product = xmlDoc.SelectSingleNode("descendant::product[@id='p1']/price");
            product.InnerText = "125";

            Console.WriteLine("\n\nDisplay the modified XML...");
            xmlDoc.Save(Console.Out);

            // save the document with the revised node
            xmlDoc.Save(@"products2.xml");
        }
    }
}

我想感谢大家,但是版主已经警告不要这样做。我提出这个问题的目的是为了开发一种方法,用准备好的字符串数组替换一个更长的节点(36个元素)的元素。这个特定的目标Xml文件不包含属性,只有两层结构:<patients><profile><ID>1</ID>...</profile>...</patients>。 - Mike Pliam
@MikePliam:你可以点赞,不应该有问题。如果它解决了你的问题,你也可以接受一个答案。关于ID元素的问题:我只能回答你在SO上所问的内容。我无法猜测你的真实情况。对于给定的XML,上面的程序是有效的。如果你的问题没有完全解决,请提出一个新的问题。 - Thomas Weller
不确定在这个论坛中“upvote”的意思是什么。无论如何,你已经回答了我提出的问题。谢谢。 - Mike Pliam
要点赞(如果有帮助),请按指向上方的三角形。要踩(如果没有帮助或者错误),请按指向下方的三角形。要接受,请点击复选标记。所有这些项目都在答案左侧。 - Thomas Weller

0

如果我理解正确,您想要更新p1中的price值。如果是这样,使用LINQ很简单。

XDocument xmlDoc = XDocument.Load(@"products.xml");
var product = xmlDoc.Descendants("product")
                  .Where(item => item.Attribute("id").Value == "p1").FirstOrDefault();
product.Element("price").Value = "125"; //new price will be 125

如果您检查xmlDoc变量,它将被更新为p1的价格为125,因此您可以保存它。当然,您可以使用LINQ创建通用方法,以便更轻松地替换任何节点,但以上内容应该给出了一种实现方式的想法。

0

不要创建元素,而是创建节点并将其附加到你代码中的产品节点。

        XmlNode priceNode = xmlDoc.CreateNode(XmlNodeType.Element, "price", string.Empty);
        priceNode.InnerText = "125";
        product.AppendChild(priceNode);

您可以在“产品节点”下重复上述代码所需的节点数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接