Reputation: 57
Given the local xml file:
<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>
I have attempted to replace the price element in product node 'p1' as follows:
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");
Problem is that the new node (price) element is simply added to the product p1 node which when saved to disk drops all of p1. What am I doing wrong?
Upvotes: 4
Views: 15380
Reputation: 59208
The main issue is in
product = root.SelectSingleNode("descendant::product[id='p1']", nsmgr);
because you're not using the variable in the following.
Next issue is in [id='p1']
, because you're accessing ID like an element, but it should be an attribute instead. Use [@id='p1']
instead.
Other things:
<price>
node directly instead of replacing a whole element.oldNode
. That node already exists.newElem
is unused.Suggested fix in your style:
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");
}
}
}
Even shorter by directly replacing the text inside the price element:
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");
}
}
}
Upvotes: 2
Reputation: 69362
If I understand correctly, you want to update the value of price
in p1
. If so, it's straightforward with 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
If you then examine the xmlDoc
variable, it'll be updated with p1
having a price of 125
so you can then save that out. Naturally, you can create generic methods using LINQ to make it easier to replace any node but the above should give an idea of one way of doing it.
Upvotes: 0
Reputation: 51
Instead of creating an element, create a node and append it to the product node you have in your code.
XmlNode priceNode = xmlDoc.CreateNode(XmlNodeType.Element, "price", string.Empty);
priceNode.InnerText = "125";
product.AppendChild(priceNode);
You may repeat the above code the no. of nodes you want under the product node.
Upvotes: 0