Insert new xml node for specific id c# - c#

How can I add new node where id is a certain value from a textbox?
This is my xml:
<Students>
<Student>
<id>111</id>
<Value>1</Value>
<information>
<data DateTime="02.04.2014 13:00:00" Value="1"/>
</information>
</Student>
</Students>
So my question is, I have a textbox where I enter the student id. After clicking on a button, I want to add a new node in information node, containing the attribute date and time in that moment.
Another thing is that I want the innerText in the node to be changed from 1 to 0 and vice-versa each new time I click. So that will be the second attribute in the node.
The next time I click, it suppose to add a new node, it will add this.
<information>
<data DateTime="02.04.2014 13:00:00" Value="1"/>
<data DateTime="02.04.2014 14:00:00" Value="0"/>
</information>
How can I do it with XmlLinq?

var xdoc = XDocument.Load(path_to_xml);
var student = xdoc.Root.Elements("Student")
.FirstOrDefault(s => (int)s.Element("id") == id);
if (student != null) // check if student was found
{
var info = student.Element("information");
if (info == null) // check if it has information element
{
info = new XElement("information");
student.Add(info); // create and add information element
}
var lastValue = info.Elements("data")
.OrderBy(d => (DateTime)d.Attribute("DateTime"))
.Select(d => (int)d.Attribute("Value"))
.LastOrDefault(); // get latest value
// create new data element
var data =
new XElement("data",
new XAttribute("DateTime", DateTime.Now.ToString("MM.dd.yyyy HH:mm:ss")),
new XAttribute("Value", lastValue == 0 ? 1 : 0));
info.Add(data); // add data element to information element
xdoc.Save(path_to_xml); // save file
}
Result:
<Students>
<Student>
<id>111</id>
<Value>1</Value>
<information>
<data DateTime="02.04.2014 13:00:00" Value="1" />
<data DateTime="02.05.2014 00:40:18" Value="0" />
</information>
</Student>
</Students>

c# has a method that will let you do this easily:
XmlNode.InsertAfter Method
Link to the actual page: http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.insertafter(v=vs.110).aspx
Code examples if you don't want to click through:
using System;
using System.IO;
using System.Xml;
public class Sample {
public static void Main() {
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" +
"<title>Pride And Prejudice</title>" +
"</book>");
XmlNode root = doc.DocumentElement;
//Create a new node.
XmlElement elem = doc.CreateElement("price");
elem.InnerText="19.95";
//Add the node to the document.
root.InsertAfter(elem, root.FirstChild);
Console.WriteLine("Display the modified XML...");
doc.Save(Console.Out);
}
}
If you need to find a specifc node to insert after, check this out
http://msdn.microsoft.com/en-us/library/h0hw012b(v=vs.110).aspx

Related

See if XML node with a certain child node's value exists - if it doesn't create a new node that has this child node with the desired value

Here is my XML file
<?xml version="1.0" encoding="UTF-8"?>
<Data>
<Month>
<Month_Number>1</Month_Number>
<Tool>
<Name>Help</Name>
<Count>40</Count>
</Tool>
</Month>
<Month>
<Month_Number>2</Month_Number>
<Tool>
<Name>Help</Name>
<Count>50</Count>
</Tool>
</Month>
</Data>
I would like to see if there is a Month which has Month_Number with the value of 3. If it doesn't exist not I would like to add a Month which has a Month_Number with the value of 3. The above XML file will turn into the following:
<Data>
<Month>
<Month_Number>1</Month_Number>
<Tool>
<Name>Help</Name>
<Count>40</Count>
</Tool>
</Month>
<Month>
<Month_Number>2</Month_Number>
<Tool>
<Name>Help</Name>
<Count>50</Count>
</Tool>
</Month>
<Month>
<Month_Number>3</Month_Number>
<Tool>
<Name>Help</Name>
<Count>50</Count>
</Tool>
</Month>
</Data>
And here is the I came up with. Code partially works by going through each month and telling me if it exists. however, it errors when creating a new node
An unhandled exception of type 'System.NullReferenceException' occurred
because of node.AppendChild(xMonth);
code:
XmlDocument tallyFile = new XmlDocument();
tallyFile.Load(tallyFilePath);
XmlNode node = tallyFile["Data"]; //mainSettingsDoc["Data"]["Month"]
foreach (XmlNode childNode in node.ChildNodes)
{
// IF MONTH EXISTS
if (childNode["Month_Number"].InnerText.Equals("3"))
{
MessageBox.Show("MONTH EXISTS!");
} // END IF MONTH EXISTS
else // IF MONTH DOESNT EXISTS
{
XmlElement xMonth = tallyFile.CreateElement(string.Empty, "Month", string.Empty);
node.AppendChild(xMonth);
MessageBox.Show("MONTH DOESNT EXIST!");
} // END IF MONTH DOESNT EXIST
} // END OF FOREACH LOOP
tallyFile.Save(tallyFilePath);
It is easier to implement by using LINQ to XML API. It is available in the .Net Framework since 2007.
c#
void Main()
{
const string inputFile = #"e:\Temp\weehee.xml";
const string outputFile = #"e:\Temp\weehee_2.xml";
XDocument xdoc = XDocument.Load(inputFile);
if (xdoc.Descendants("Month")
.Where(x => x.Element("Month_Number").Value.Equals("3")).Count() == 0)
{
xdoc.Root.Add(new XElement("Month",
new XElement("Month_Number", "3"),
new XElement("Tool",
new XElement("Name", "Help"),
new XElement("Count", "50")
)));
}
xdoc.Save(outputFile);
}

SelectSingleNode Node InnerText property not correct

I am attempting to get the value of a specific node for each parent element found.
In the example I want to return each students First Name.
Instead I am getting the first elements name in each instance. The InnerText of a Student is correct but the InnerText of FirstName is always Alex.
var xml = #"<School>
<Students>
<Student>
<FirstName>Alex</FirstName>
<LastName>Smith</LastName>
<Grade>11</Grade>
</Student>
<Student>
<FirstName>Joanne</FirstName>
<LastName>Robins</LastName>
<Grade>12</Grade>
</Student>
<Student>
<FirstName>Steve</FirstName>
<LastName>Baker</LastName>
<Grade>11</Grade>
</Student>
</Students>
<Teachers>
<Teacher>
<FirstName>George</FirstName>
<LastName>Roberts</LastName>
<Grade>11</Grade>
</Teacher>
<Teacher>
<FirstName>Amanda</FirstName>
<LastName>Walker</LastName>
<Grade>12</Grade>
</Teacher>
<Teacher>
<FirstName>Tracey</FirstName>
<LastName>Smith</LastName>
<Grade>12</Grade>
</Teacher>
</Teachers>
</School>";
var doc = new XmlDocument();
doc.LoadXml(xml);
var resourceTypeNodes = doc.GetElementsByTagName("Student");
var resourceTypesIterator = resourceTypeNodes.GetEnumerator();
while (resourceTypesIterator != null && resourceTypesIterator.MoveNext())
{
var resourceTypeNode = resourceTypesIterator.Current as XmlNode;
var typeNameElement = resourceTypeNode.SelectSingleNode("//FirstName");
Console.WriteLine(resourceTypeNode.InnerXml);
Console.WriteLine(typeNameElement.InnerText);
}
This is the output of the above code.
<FirstName>Alex</FirstName><LastName>Smith</LastName><Grade>11</Grade>
Alex
<FirstName>Joanne</FirstName><LastName>Robins</LastName><Grade>12</Grade>
Alex
<FirstName>Steve</FirstName><LastName>Baker</LastName><Grade>11</Grade>
Alex
What am I missing?
Because you're using //FirstName XPath expression, that will always return first node from root, it doesn't matter if you invoke on children. Just change this:
var typeNameElement = resourceTypeNode.SelectSingleNode("//FirstName");
To this:
var typeNameElement = resourceTypeNode.SelectSingleNode("FirstName");
Moreover is there any specific reason you're manually using IEnumerator? You may simplify your code with foreach:
foreach (XmlNode resourceTypeNode in doc.GetElementsByTagName("Student"))
{
var typeNameElement = resourceTypeNode.SelectSingleNode("FirstName");
Console.WriteLine(resourceTypeNode.InnerXml);
Console.WriteLine(typeNameElement.InnerText);
}

Linq to XML... null and missing elements

I have an XML file
<Person>
<PersonItem id="0">
<Time>1/8/2014</Time>
<Step><![CDATA[Normal]]></Step>
<HasAddress/>
<Address/>
</PersonItem>
<PersonItem id="1">
<Time>1/8/2014 3:21:45 PM</Time>
<Step><![CDATA[Normal]]></Step>
<HasAddress/>
<Address/>
</PersonItem>
<PersonItem id="2">
<Time>1/8/2014</Time>
<Step><![CDATA[Normal]]></Step>
<HasAddress>Main</HasAddress>
<Address>
<AddressItem id="0" location=5>
<Address>15 Oak</Address>
</AddressItem>
<AddressItem id="1" location=7>
<Address>12 Maple</Address>
</AddressItem>
<AddressItem id="2" location=8>
<Address>30 Beech</Picture>
</AddressItem>
</Address>
</PersonItem>
</Person>
I want to put to retrieve the information and send some of it to a database. I've tried several different ways of dealing with this and I believe I'm close. Here is the Linq I tried.
public void DoIt(fileName)
{
XElement xml = XElement.Load(fileName);
var items = from item in xml.Elements("PersonItem")
where (from x in item.Elements("HasAddress")
where x.Element("HasAddress") != null
select x).Any()
select item;
Array.ForEach(items.ToArray(),
o=>Console.WriteLine(o.Element("Time").Value));
Console.ReadLine();
}
The problem is nothing is being returned.
Could be just a typo but in your xml file there is this tag error.
<Address>30 Beech</Picture>
which should be:
<Address>30 Beech</Address>
Try this:
XElement xml = XElement.Load(fileName);
var items = xml.Descendants("PersonItem")
.Where(x => (string)x.Element("HasAddress") != null)
.Select(x => x);
XDocument xml = XDocument.Load("Input.xml");
var items = from item in xml.Root.Elements("PersonItem")
where !string.IsNullOrEmpty((string)item.Element("HasAddress"))
select item;
For your sample XML document returns only the last PersonItem element.

Get InnerText from Collection

Is there a way to get the innertext of a node when the node is inside a collection
Currently i have this
Collection<string> DependentNodes = new Collection<string>();
foreach (XmlNode node in nodes)
{
for (int i = 0; i < node.ChildNodes.Count; i++)
{
DependentNodes.Add(node.ChildNodes[i].InnerXml);
//the reason i'm using InnerXml is that it will return all the child node of testfixture in one single line,then we can find the category & check if there's dependson
}
}
string selectedtestcase = "abc_somewords";
foreach (string s in DependentNodes)
{
if(s.Contains(selectedtestcase))
{
MessageBox.Show("aaa");
}
}
When i debug string s or the index has this inside of it[in a single line]
<testfixture name="1" description="a">
<categories>
<category>abc_somewords</category>
</categories>
<test name="a" description="a">
<dependencies>
<dependson typename="dependsonthis" />
</dependencies>
</test>
</testfixture>
What i'm trying to do is when we reach "testfixture 1" it will find "abc_somewords" & search the "dependson typename"node(if any) and get the "typename"(which is "dependonthis").
Could you use linq to xml. Something like the below might be a decent start
xml.Elements("categories").Where(x => x.Element("category").Value.Contains(selectedtestcase));
This is off the top of my head so might will need refining
P.S. Use XElement.Load or XElement.Parse to get your xml into XElements
Since you already working with XmlNode you could use a XPath expression to select the desired textfixture node, and select the dependency value:
XmlDocument doc = // ...
XmlNode node = doc.SelectSingleNode("//testfixture[contains(categories/category, \"abc\")]/test/dependencies/dependson/");
if (node != null)
{
MessageBox.Show(node.Attributes["typename"]);
}
This selects the dependson node which belongs to a testfixture node with a category containing "abc". node.Attributes["typename"] will return the value of the typename attribute.
Edited:
Updated XPath expression to the more specific question information
Assumptions
As you are looping in your code and wanting to create a collection I'm assuming the actual Xml File has several testfixture nodes inside such as the below assumed example:
<root>
<testfixture name="1" description="a">
<categories>
<category>abc_somewords</category>
</categories>
<test name="a" description="a">
<dependencies>
<dependson typename="dependsonthis" />
</dependencies>
</test>
</testfixture>
<testfixture name="2" description="a">
<categories>
<category>another_value</category>
</categories>
<test name="b" description="a">
<dependencies>
<dependson typename="secondentry" />
</dependencies>
</test>
</testfixture>
<testfixture name="3" description="a">
<categories>
<category>abc_somewords</category>
</categories>
<test name="c" description="a">
<dependencies>
<dependson typename="thirdentry" />
</dependencies>
</test>
</testfixture>
</root>
The Code using Linq to Xml
To use Linq you must reference the following name spaces:
using System.Linq;
using System.Xml.Linq;
Using Linq To Xml on the above assumed xml file structure would look like this:
// To Load Xml Content from File.
XDocument doc1 = XDocument.Load(#"C:\MyXml.xml");
Collection<string> DependentNodes = new Collection<string>();
var results =
doc1.Root.Elements("testfixture")
.Where(x => x.Element("categories").Element("category").Value.Contains("abc_somewords"))
.Elements("test").Elements("dependencies").Elements("dependson").Attributes("typename").ToArray();
foreach (XAttribute attribute in results)
{
DependentNodes.Add(attribute.Value.Trim());
}
Result
The resulting Collection will contain the following:
As you can see, only the text of the typename attribute has been extracted where the dependson nodes where in a testfixture node which contained a category node with the value of abc_somewords.
Additional Notes
If you read the xml from a string you can also use this:
// To Load Xml Content from a string.
XDocument doc = XDocument.Parse(myXml);
If your complete Xml structure is different, feel free to post it and I change the code to match.
Have Fun.
I don't know what is "nodes" you are using.
Here is code with your requirement(What I understood).
Collection<XmlNode> DependentNodes = new Collection<XmlNode>();
XmlDocument xDoc = new XmlDocument();
xDoc.Load(#"Path_Of_Your_xml");
foreach (XmlNode node in xDoc.SelectNodes("testfixture")) // Here I am accessing only root node. Give Xpath if ur requrement is changed
{
for (int i = 0; i < node.ChildNodes.Count; i++)
{
DependentNodes.Add(node.ChildNodes[i]);
}
}
string selectedtestcase = "abc_somewords";
foreach (var s in DependentNodes)
{
if (s.InnerText.Contains(selectedtestcase))
{
Console.Write("aaa");
}
}
using System;
using System.Xml;
namespace ConsoleApplication6
{
class Program
{
private const string XML = "<testfixture name=\"1\" description=\"a\">" +
"<categories>" +
"<category>abc_somewords</category>" +
"</categories>" +
"<test name=\"a\" description=\"a\">" +
"<dependencies>" +
"<dependson typename=\"dependsonthis\" />" +
"</dependencies>" +
"</test>" +
"</testfixture>";
static void Main(string[] args)
{
var document = new XmlDocument();
document.LoadXml(XML);
var testfixture = document.SelectSingleNode("//testfixture[#name = 1]");
var category = testfixture.SelectSingleNode(".//category[contains(text(), 'abc_somewords')]");
if(category != null)
{
var depends = testfixture.SelectSingleNode("//dependson");
Console.Out.WriteLine(depends.Attributes["typename"].Value);
}
Console.ReadKey();
}
}
}
Output: dependsonthis

Filter XDocument more efficiently

I would like to filter with high performance XML elements from an XML document.
Take for instance this XML file with contacts:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="asistentes.xslt"?>
<contactlist evento="Cena Navidad 2010" empresa="company">
<contact type="1" id="1">
<name>Name1</name>
<email>xxxx#zzzz.es</email>
<confirmado>SI</confirmado>
</contact>
<contact type="1" id="2">
<name>Name2</name>
<email>xxxxxxxxx#zzzze.es</email>
<confirmado>Sin confirmar</confirmado>
</contact>
</contaclist>
My current code to filter from this XML document:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = #" the xml above";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("contact")) {
Console.WriteLine(element);
var id = element.Attribute("id").Value;
var valor = element.Descendants("confirmado").ToList()[0].Value;
var email = element.Descendants("email").ToList()[0].Value;
var name = element.Descendants("name").ToList()[0].Value;
if (valor.ToString() == "SI") { }
}
}
}
What would be the best way to optimize this code to filter on <confirmado> element content?
var doc = XDocument.Parse(xml);
var query = from contact in doc.Root.Elements("contact")
let confirmado = (string)contact.Element("confirmado")
where confirmado == "SI"
select new
{
Id = (int)contact.Attribute("id"),
Name = (string)contact.Element("name"),
Email = (string)contact.Element("email"),
Valor = confirmado
};
foreach (var contact in query)
{
...
}
Points of interest:
doc.Root.Elements("contact") selects only the <contact> elements in the document root, instead of searching the whole document for <contact> elements.
The XElement.Element method returns the first child element with the given name. No need to convert the child elements to a list and take the first element.
The XElement and XAttribute classes provide a wide selection of convenient conversion operators.
You could use LINQ:
foreach (XElement element in doc.Descendants("contact").Where(c => c.Element("confirmado").Value == "SI"))

Categories