XML with C# strange behaviour - c#

I am new to C# and XML and I need help with following code, I have created an Element thru C# code and wanted to insert City Element in a XML file at two places and coded the following code. But C# code inserts Element only in 4th node not in 1st node, I am not able to figure it out what am I missing.Any help will be appreciated. It is working fine if I keep only one INSERTAFTER.
XmlElement childElement = doc.CreateElement("City"); // Creation of <City> ....... <City>
childElement.InnerText = "Hyderabad";// Adding Value <City> Hyderabad </City>
XmlNode SelectNode = doc.SelectSingleNode("Group/user"); //where to add XPATH expression
SelectNode.InsertAfter(childElement, SelectNode.LastChild);//selects 1st "user" node lastchild and insert after
XmlNode refNode = doc.SelectSingleNode("Group/user[4]");// Indicating 4 Node in XML file
refNode.InsertAfter(childElement, refNode.LastChild); //selects 4th "user" node lastchild and insert after
XML file:
<?xml version="1.0" encoding="utf-8"?>
<Group>
<user>
<Name age="39">John Hay</Name>
<RollNo>01</RollNo>
=== <City>Hyderabad</City> ===> is missing
</user>
<user>
<Name age="11">Ramsey</Name>
<RollNo>02 </RollNo>
</user>
<user>
<Name age="16">Roshan</Name>
<RollNo>03</RollNo>
</user>
<user>
<Name age="42">Rahiman</Name>
<RollNo>04</RollNo>
<City>Hyderabad</City> ==> This is fine.
</user>
</Group>
Can I reference any ELEMENT (Ex. RollNo) based on its Text.value and insert one more element ?
Thanks

You are moving the same instance of your city-node around your xml-document. You need to instantiate another one:
XmlElement childElement = doc.CreateElement("City"); // Creation of <City> ....... <City>
childElement.InnerText = "Hyderabad";// Adding Value <City> Hyderabad </City>
XmlNode SelectNode = doc.SelectSingleNode("Group/user"); //where to add XPATH expression
SelectNode.InsertAfter(childElement, SelectNode.LastChild);//selects 1st "user" node lastchild and insert after
childElement = doc.CreateElement("City"); // Creation of <City> ....... <City>
childElement.InnerText = "Hyderabad";// Adding Value <City> Hyderabad </City>
XmlNode refNode = doc.SelectSingleNode("Group/user[4]");// Indicating 4 Node in XML file
refNode.InsertAfter(childElement, refNode.LastChild); //selects 4th "user" node lastchild and insert after
/Edit: Cloning your city-node might be a bit more appropriate.
https://msdn.microsoft.com/de-de/library/system.xml.xmlelement.clonenode(v=vs.110).aspx
XmlElement childElement = doc.CreateElement("City"); // Creation of <City> ....... <City>
childElement.InnerText = "Hyderabad";// Adding Value <City> Hyderabad </City>
XmlNode SelectNode = doc.SelectSingleNode("Group/user"); //where to add XPATH expression
SelectNode.InsertAfter(childElement, SelectNode.LastChild);//selects 1st "user" node lastchild and insert after
XmlNode refNode = doc.SelectSingleNode("Group/user[4]");// Indicating 4 Node in XML file
refNode.InsertAfter(childElement.CloneNode(true), refNode.LastChild); //selects 4th "user" node lastchild and insert after

You can easily access specific nodex using text() in XPath...
Take a look at this example:
//get roll node by it's text
var rollNode = doc.SelectSingleNode("//user/RollNo[text()='03']");
//user to whom roll is child
var parentUserNode = rollNode.ParentNode;
//add some nodex
parentUserNode.InsertAfter(childElement, parentUserNode.LastChild);

This would be much easier using the LINQ to XML API rather than the old XmlDocument API. For example, to do this by order:
var doc = XDocument.Parse(xml);
var hyderabad = new XElement("City", "Hyderabad");
doc.Descendants("user").First().Add(hyderabad);
doc.Descendants("user").Skip(3).First().Add(hyderabad);
Or to select by RollNo:
var doc = XDocument.Parse(xml);
var roll4 = doc.Descendants("user").Single(x => (int) x.Element("RollNo") == 4);
roll4.Add(new XElement("City", "Hyderabad"));
See this fiddle for a demo.

Related

Searching through XML to find a list of items

I have an xml doc as such:
<?xml version="1.0" encoding="utf-8" ?>
<Categories>
<Category>
<Name>Fruit</Name>
<Items>
<Item>Apple</Item>
<Item>Banana</Item>
<Item>Peach</Item>
<Item>Strawberry</Item>
</Items>
</Category>
<Category>
<Name>Vegetable</Name>
<Items>
<Item>Carrots</Item>
<Item>Beets</Item>
<Item>Green Beans</Item>
<Item>Bell Pepper</Item>
</Items>
</Category>
<Category>
<Name>Seafood</Name>
<Items>
<Item>Crab</Item>
<Item>Lobster</Item>
<Item>Shrimp</Item>
<Item>Oysters</Item>
<Item>Salmon</Item>
</Items>
</Category>
</Categories>
I would like to be able to search on a term such as Category.Name = Fruit and get back the list of the Fruit Items.
Here is the incomplete code I've started so far:
string localPath = Server.MapPath("~/App_Data/Foods.xml");
XmlDocument doc = new XmlDocument();
doc.Load(localPath);
XmlNodeList list = doc.SelectNodes("Categories");
//Do something here to search the category names and get back the list of items.
This is my first attempt at parsing through XML so I'm a bit lost. Note: the application I am working on uses .Net 2.0
I'd suggest to read about XPath as you're limited to .NET 2.0, moreover XPath is very useful to work with XML even in more general context (not limited to .NET platform only).
In this particular case XPath become useful because SelectNodes() and SelectSingleNode() method accept XPath string as parameter. For example, to get all <Item> that corresponds to category name "Fruit" :
string localPath = Server.MapPath("~/App_Data/Foods.xml");
XmlDocument doc = new XmlDocument();
doc.Load(localPath);
XmlNodeList items = doc.SelectNodes("Categories/Category[Name='Fruit']/Items/Item");
foreach(XmlNode item in items)
{
Console.WriteLine(item.InnerText);
}
You can see XPath as a path, similar to file path in windows explorer. I'd try to explain only the bit that is different from common path expression in the above sample, particularly this bit :
...\Category[Name='Fruit']\...
the expression within square-brackets is a filter which say search for <Category> node having child node <Name> equals "Fruit".
You are on the right path. However, you would need to load the 'Categories' node first, then you can get it's child nodes.
I have added a filter to return only nodes where the name is "Fruit".
XmlNode cat = doc.SelectSingleNode("Categories");
var list = cat.SelectNodes("Category").Cast<XmlNode>()
.Where(c => c.SelectSingleNode("Name").InnerText == "Fruit");
foreach ( XmlNode item in list )
{
// process each node here
}

c#: Add element to second nesting in xml

I have a XML which looks like:
<users>
<user id="0">
<name>John</name>
<lastName>Smith</lastName>
<bills>
<bill id="0">
<name>Water</name>
<forMonth>2013-12-01</forMonth>
<money>235</money>
<lastDayToPay>2014-01-02</lastDayToPay>
<payed>False</payed>
</bill>
<bill id="1">
<name>Telephone</name>
<forMonth>2013-11-01</forMonth>
<money>690</money>
<lastDayToPay>2014-01-01</lastDayToPay>
<payed>True</payed>
</bill>
</bills>
</user>
How can i add new bill for the user, i have problem accessing "bills" node and adding element to it. I'm using c#.
use following code
XmlDocument myDocument = new XmlDocument();
myDocument.Load(XMLFile);
XmlNode newNode = myDocument.CreateElement("bill");
//add values;
var requiredNode = myDocument.ChildNodes.OfType<XmlElement>().Where(o => o.Name == "bills").First();
requiredNode.AppendChild(newNode);
myDocument.Save(XMLFile);

how can append to xml

i have this xml.
<project>
<user>
<id>1</id>
<name>a</name>
</user>
<user>
<id>2</id>
<name>b</name>
</user>
</project>
now how can append a new element like this between element <project></project>
<user>
<id>3</id>
<name>c</name>
</user>
string xml =
#"<project>
<user>
<id>1</id>
<name>a</name>
</user>
<user>
<id>2</id>
<name>b</name>
</user>
</project>";
XElement x = XElement.Load(new StringReader(xml));
x.Add(new XElement("user", new XElement("id",3),new XElement("name","c") ));
string newXml = x.ToString();
If you mean using C# then probably the simplest way is to load the xml up into an XmlDocument object and then add a node representing the additional element.
e.g. something like:
string filePath = "original.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filePath);
XmlElement root = xmlDoc.DocumentElement;
XmlNode nodeToAdd = doc.CreateElement(XmlNodeType.Element, "user", null);
XmlNode idNode = doc.CreateElement(XmlNodeType.Element, "id", null);
idNode.InnerText = "1";
XmlNode nameNode = doc.CreateElement(XmlNodeType.Element, "name", null);
nameNode.InnerText = "a";
nodeToAdd.AppendChild(idNode);
nodeToAdd.AppendChild(nameNode);
root.AppendChild(nodeToAdd);
xmlDoc.Save(filePath); // Overwrite or replace with new file name
But you haven't said where the xml fragments are - in files/strings?
If you have the below XML file:
<CATALOG>
<CD>
<TITLE> ... </TITLE>
<ARTIST> ... </ARTIST>
<YEAR> ... </YEAR>
</CD>
</CATALOG>
and you have to add another <CD> node with all its child nodes:
using System.Xml; //use the xml library in C#
XmlDocument document = new XmlDocument(); //creating XML document
document.Load(#"pathOfXmlFile"); //load the xml file contents into the newly created document
XmlNode root = document.DocumentElement; //points to the root element (catalog)
XmlElement cd = document.CreateElement("CD"); // create a new node (CD)
XmlElement title = document.CreateElement("TITLE");
title.InnerXML = " ... "; //fill-in the title value
cd.AppendChild(title); // append title to cd
XmlElement artist = document.CreateElement("ARTIST");
artist.InnerXML = " ... ";
cd.AppendChild(artist);
XmlElement year = document.CreateElement("YEAR");
year.InnerXML = " ... ";
cd.AppendChild(year);
root.AppendChild(cd); // append cd to the root (catalog)
document.save(#"savePath");//save the document

XML string to XML document

I have a whole XML document in a String which i need to convert to a XML document and parse tags in the document
This code sample is taken from csharp-examples.net, written by Jan Slama:
To find nodes in an XML file you can use XPath expressions. Method XmlNode.Selec­tNodes returns a list of nodes selected by the XPath string. Method XmlNode.Selec­tSingleNode finds the first node that matches the XPath string.
XML:
<Names>
<Name>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
</Name>
<Name>
<FirstName>James</FirstName>
<LastName>White</LastName>
</Name>
</Names>
CODE:
XmlDocument xml = new XmlDocument();
xml.LoadXml(myXmlString); // suppose that myXmlString contains "<Names>...</Names>"
XmlNodeList xnList = xml.SelectNodes("/Names/Name");
foreach (XmlNode xn in xnList)
{
string firstName = xn["FirstName"].InnerText;
string lastName = xn["LastName"].InnerText;
Console.WriteLine("Name: {0} {1}", firstName, lastName);
}
Using Linq to xml
Add a reference to System.Xml.Linq
and use
XDocument.Parse(string xmlString)
Edit: Sample follows, xml data (TestConfig.xml)..
<?xml version="1.0"?>
<Tests>
<Test TestId="0001" TestType="CMD">
<Name>Convert number to string</Name>
<CommandLine>Examp1.EXE</CommandLine>
<Input>1</Input>
<Output>One</Output>
</Test>
<Test TestId="0002" TestType="CMD">
<Name>Find succeeding characters</Name>
<CommandLine>Examp2.EXE</CommandLine>
<Input>abc</Input>
<Output>def</Output>
</Test>
<Test TestId="0003" TestType="GUI">
<Name>Convert multiple numbers to strings</Name>
<CommandLine>Examp2.EXE /Verbose</CommandLine>
<Input>123</Input>
<Output>One Two Three</Output>
</Test>
<Test TestId="0004" TestType="GUI">
<Name>Find correlated key</Name>
<CommandLine>Examp3.EXE</CommandLine>
<Input>a1</Input>
<Output>b1</Output>
</Test>
<Test TestId="0005" TestType="GUI">
<Name>Count characters</Name>
<CommandLine>FinalExamp.EXE</CommandLine>
<Input>This is a test</Input>
<Output>14</Output>
</Test>
<Test TestId="0006" TestType="GUI">
<Name>Another Test</Name>
<CommandLine>Examp2.EXE</CommandLine>
<Input>Test Input</Input>
<Output>10</Output>
</Test>
</Tests>
C# usage...
XElement root = XElement.Load("TestConfig.xml");
IEnumerable<XElement> tests =
from el in root.Elements("Test")
where (string)el.Element("CommandLine") == "Examp2.EXE"
select el;
foreach (XElement el in tests)
Console.WriteLine((string)el.Attribute("TestId"));
This code produces the following output:
0002
0006
Depending on what document type you want you can use XmlDocument.LoadXml or XDocument.Load.
Try this code:
var myXmlDocument = new XmlDocument();
myXmlDocument.LoadXml(theString);

deleting specific node in xml

I need to delete specific employee node and also its child node based on the value of id.
For example, here I need to delete employee tag with id="2".
<company>
<employee>
<id>1</id>
<name>sa</name>
</employee>
<employee>
<id>2</id>
<name>ssa</name>
</employee>
</company>
Assuming you have loaded that into an XmlDocument named doc:
XmlElement el = (XmlElement)doc.SelectSingleNode("/company/employee[id=2]");
if(el != null) { el.ParentNode.RemoveChild(el); }
Try this one
XmlDocument xmlDoc = new XmlDocument();
XmlNode nodeToDelete = xmlDoc.SelectSingleNode("/root/XMLFileName[#ID="+nodeId+"]");
if (nodeToDelete != null)
{
nodeToDelete.ParentNode.RemoveChild(nodeToDelete);
}
xmlDoc.Save("XMLFileName.xml")

Categories