I am trying to read an xml file into a treeview structure on C#. The Treeview is supposed to show the root node as "Students" and under that display each student number, that expands into all the other details of the students. I am unsure how to execute this properly and integrate the Course table as well.
I wrote the XML file coding (File name ReadXml) as following:
<?xml version="1.0" encoding="UTF-8"?>
<STUDENTS>
<Course>
<ID>2</ID>
<Course_Name>Bcom Acounting</Course_Name>
<Course_Description>For Accountants</Course_Description>
</Course>
<Course>
<ID>3</ID>
<Course_Name>Engineering</Course_Name>
<Course_Description>For students not liking the social</Course_Description>
</Course>
<Course>
<ID>1</ID>
<Course_Name>Bcom Informatics</Course_Name>
<Course_Description>Business Computer Science</Course_Description>
</Course>
<Students>
<ID>1</ID>
<Name>Henk</Name>
<Initials>HW</Initials>
<Surname>Pretorius</Surname>
<StudentNumber>91404411</StudentNumber>
<YearOfRegistration>2014</YearOfRegistration>
<Gender>M</Gender>
<Course>1</Course>
</Students>
<Students>
<ID>2</ID>
<Name>Jane</Name>
<Initials>JL</Initials>
<Surname>Hlope</Surname>
<StudentNumber>22223333</StudentNumber>
<YearOfRegistration>2014</YearOfRegistration>
<Gender>F</Gender>
<Course>1</Course>
</Students>
<Students>
<ID>3</ID>
<Name>Samuel</Name>
<Initials>SM</Initials>
<Surname>Klopper</Surname>
<StudentNumber>12341233</StudentNumber>
<YearOfRegistration>2014</YearOfRegistration>
<Gender>M</Gender>
<Course>2</Course>
</Students>
<Students>
<ID>4</ID>
<Name>Sue</Name>
<Initials>SS</Initials>
<Surname>Callahan</Surname>
<StudentNumber>12333222</StudentNumber>
<YearOfRegistration>2014</YearOfRegistration>
<Gender>F</Gender>
<Course>2</Course>
</Students>
<Students>
<ID>5</ID>
<Name>Koos</Name>
<Initials>KK</Initials>
<Surname>Klopper</Surname>
<StudentNumber>12334455</StudentNumber>
<YearOfRegistration>2014</YearOfRegistration>
<Gender>M</Gender>
<Course>1</Course>
</Students>
</STUDENTS>
The method I used in C# was the following:
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Title = "Open XML Document";
dlg.Filter = "XML Files (*.xml)|*.xml";
dlg.FileName = "ReadXml.xml";
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load(dlg.FileName);
treeView1.Nodes.Clear();
treeView1.Nodes.Add(new TreeNode(xDoc.DocumentElement.Name));
TreeNode tNode = new TreeNode();
tNode = (TreeNode)treeView1.Nodes[0];
addTreeNode(xDoc.DocumentElement, tNode);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void addTreeNode(XmlNode xmlNode, TreeNode treeNode)
{
XmlNode xNode;
TreeNode tNode;
XmlNodeList xNodeList;
if (xmlNode.HasChildNodes)
{
xNodeList = xmlNode.ChildNodes;
for (int x = 0; x <= xNodeList.Count -1; x++)
{
xNode = xmlNode.ChildNodes[x];
treeNode.Nodes.Add(new TreeNode(xNode.Name));
tNode = treeNode.Nodes[x];
addTreeNode(xNode, tNode);
}
}
else
treeNode.Text = xmlNode.OuterXml.Trim();
}
The Code I used in C# doesn't bring the results I am looking for. Would appreciate some help.
thanks
Related
I have following SOAP response. Using my code I could only extract one element.
and then it throws null reference exception error.
How do I extract Item->key and Item->value in to my dictionary in the function in C#?
Following is a part of the function which i use to extract data from.
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP- ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:Magento" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:callResponse>
<callReturn SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">state</key>
<value xsi:type="xsd:string">processing</value>
</item>
<item>
<key xsi:type="xsd:string">status</key>
<value xsi:type="xsd:string">processing</value>
</item>
<item>
<key xsi:type="xsd:string">protect_code</key>
<value xsi:type="xsd:string">ba8dd7</value>
</item>
<item>
<key xsi:type="xsd:string">shipping_firstname</key>
<value xsi:type="xsd:string">Roshan</value>
</item>
<item>
<key xsi:type="xsd:string">billing_name</key>
<value xsi:type="xsd:string">Roshan India</value>
</item>
<item>
<key xsi:type="xsd:string">shipping_name</key>
<value xsi:type="xsd:string">Roshan India</value>
</item>
<item>
<key xsi:type="xsd:string">order_id</key>
<value xsi:type="xsd:string">2</value>
</item>
</item>
</callReturn>
</ns1:callResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The codes,
try
{
XmlDocument xdoc = new XmlDocument();//xml doc used for xml parsing
xdoc.LoadXml(content); // --------> THIS IS WHERE I PASS SOAP RESPONSE
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("SOAP-ENV:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("//item");
int nodes = xNodelst.Count;
dynamic item;
Dictionary<string, string> items = new Dictionary<string, string>();
foreach (XmlNode xn in xNodelst)
{
XmlNode itemnode = xn["item"];
if (itemnode != null) {
string key = itemnode["key"].InnerText;
string value = itemnode["value"].InnerText;
items.Add(key, value);
}
}
var ss = items;
return "";
}
catch (Exception e) {
return e.Message;
}
The xml has two level of the tag item. So you have to make sure you treat each level of the item tag separately. I used xml linq to get data and put results into a dictionary.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement root = doc.Root;
XNamespace ns = root.GetDefaultNamespace();
XElement firstItem = doc.Descendants(ns + "item").FirstOrDefault();
Dictionary<string, string> dictItems = firstItem.Descendants(ns + "item")
.GroupBy(x => (string)x.Element("key"), y => (string)y.Element("value"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}
I finally managed to make this work. I added all the namespaces in namespace manager variable. And used xPath to navigate through the XML as below,
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("//item[#xsi:type]",nsmgr);
This helped me to resolve my problem. I'm posting this here in case if anyone having similar issue.
Cheers!
string soapString = #"<soapenv:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:urn=""urn:Magento""><soapenv:Header/><soapenv:Body><urn:call soapenv:encodingStyle=""http://schemas.xmlsoap.org/soap/encoding/""><sessionId xsi:type=""xsd:string"">"+ssid +#"</sessionId><resourcePath xsi:type=""xsd:string"">sales_order.list</resourcePath><args xsi:type=""xsd:anyType""></args></urn:call></soapenv:Body></soapenv:Envelope>";
HttpResponseMessage response = await PostXmlRequest("http://localhost/M1/api.php", soapString);
string content = await response.Content.ReadAsStringAsync();
XmlDocument xdoc = new XmlDocument();//xml doc used for xml parsing
xdoc.LoadXml(content);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("ns1", "urn:Magento");
nsmgr.AddNamespace("ns2", "http://xml.apache.org/xml-soap");
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
nsmgr.AddNamespace("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
nsmgr.AddNamespace("encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("//item[#xsi:type]",nsmgr);
int nodes = xNodelst.Count;
Dictionary<int, IDictionary> items = new Dictionary<int, IDictionary>();
int n = 0;
foreach (XmlNode xn in xNodelst)
{
XmlNode itemnode = xn;
Dictionary<string, string> itemData = new Dictionary<string, string>();
foreach (XmlNode xn1 in itemnode)
{
string key = xn1["key"].InnerText;
string value = xn1["value"].InnerText;
itemData.Add(key, value);
}
items.Add(n, itemData);
n++;
}
return items;
I have this xml file:
<students>
<student>
<name>Mark</name>
<age>20</age>
</student>
<student>
<name>Adam</name>
<age>32</age>
</student>
I want to change the age of Adam, for that I did:
XmlDocument doc = new XmlDocument();
doc.Load("/path/to/sutdents.xml");
XmlNode n = doc.SelectSingleNode("/students/student[name='" + student.name +
"']");
if (n != null)
{
n.SelectNodes("age").Item(0).FirstChild.Value = new_value;
}
but I get "n" as a null value.
I tried another way to do that by searching for the node using foreach and make change, but I get Null Exception:
XmlDocument doc = new XmlDocument();
doc.Load("/path/to/sutdents.xml");
XmlNodeList nodes = doc.SelectNodes("/students/student");
foreach (XmlNode node in nodes)
{
if (node.FirstChild.InnerText == student.name)
{
node.SelectSingleNode("age").InnerText = new_value;
}
}
what I missing here please?
Thanks in advance
I tried to run your code. that is work! check what is the value of student.name.
XML [sutdents.xml]
<?xml version="1.0" standalone="yes"?>
<students>
<student>
<name>Mark</name>
<age>20</age>
</student>
<student>
<name>Adam</name>
<age>32</age>
</student>
</students>
sample code:
XmlDocument doc = new XmlDocument();
doc.Load("sutdents.xml");
XmlNode n = doc.SelectSingleNode("/students/student[name='Adam']");
if (n != null)
{
n.SelectNodes("age").Item(0).FirstChild.Value = "44";
}
doc.Save("sutdents.xml");
i hope this helps;
When Using foreach:
XmlNodeList studentsList = doc.ChildNodes[0].SelectNodes("student");
foreach (XmlNode node in studentsList)
{
if (node.ChildNodes[0].InnerText == student.name) //name
{
node.ChildNodes[1].InnerText = new_value; //age
}
}
Your Code is Correct here is a screen shot of your debugged code with same xml and mentioned code, check you are passing student.name with correct value and case sensitive.
Try this using XDocument:
private static void UpdateXML(string xmlpath, string studentName, int age)
{
var doc = XDocument.Load(xmlpath);
//Get student
var student = doc.Descendants("student").Where(att => att.Element("name") != null && att.Element("name").Value.Equals(studentName)).FirstOrDefault();
if (student != null)
student.Element("age").Value = age.ToString();
doc.Save(xmlpath);
}
I have written a code that reads an XMl file and then construct a tree view of XML file using the nodes name. I would like to know how could I have attributes instead of the components(nodes) name?
For example, in the following XML file instead of action(s) I would like to print in the tree view copy or paste, etc, except for the first two parent nodes (Report and test).
XML file:
<Report version="2.1" xmlns="http://www.froglogic.com/XML2">
<test name="Example">
<action name="delet">
this is delet
</action>
<action name="copy">
this is copy
</action>
<action name="paste">
this is paste
</action>
<action name="manipulate">
this is manipulate
</action>
<action name="copy">
this is copy
</action>
<action name="paste">
this is paste
</action>
<action name="manipulate">
this is manipulate
</action>
<action name="change">
this is change
</action>
</test>
</Report>
and the C# code:
private void File2_load(object sender, EventArgs e)
{
try
{
XmlDocument doc = new XmlDocument();
doc.Load(File2Path.Text);
treeView2.Nodes.Clear();
treeView2.Nodes.Add(new TreeNode(doc.DocumentElement.Name));
TreeNode tNode = new TreeNode();
tNode = treeView2.Nodes[0];
AddNode(doc.DocumentElement, tNode);
treeView2.ExpandAll();
treeView2.CheckBoxes = true;
}
catch (XmlException xmlEx)
{
MessageBox.Show(xmlEx.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Update
private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode)
{
XmlNode xNode;
TreeNode tNode;
XmlNodeList nodeList;
int i;
if (inXmlNode.HasChildNodes)
{
nodeList = inXmlNode.ChildNodes;
for (i = 0; i <= nodeList.Count - 1; i++)
{
xNode = inXmlNode.ChildNodes[i];
inTreeNode.Nodes.Add(new TreeNode(xNode.Name));
tNode = inTreeNode.Nodes[i];
AddNode(xNode, tNode);
}
}
else
{
inTreeNode.Text = (inXmlNode.OuterXml).Trim();
}
}
Change
xNode = inXmlNode.ChildNodes[i];
inTreeNode.Nodes.Add(new TreeNode(xNode.Name));
to
xNode = inXmlNode.ChildNodes[i];
TreeNode childTreeNode;
if (xNode.LocalName == "action" && xNode.NodeType == XmlNodeType.Element)
{
childTreeNode = new TreeNode(xNode.Attributes["name"].Value);
}
else
{
childTreeNode = new TreeNode(xNode.Name);
}
inTreeNode.Nodes.Add(childTreeNode);
You might want to add further checks on NodeType and LocalName, depending on the complexity of your XML input and the possible nodes that can be contained in the XML input.
I m trying to strip my XML and keep only nodes which are in the String Keep array
Input XML is
<Employees>
<Employee>
<EmpId>1</EmpId>
<Name>Sam</Name>
<Sex>Male</Sex>
<Address>
<Country>USA</Country>
<Zip>95220</Zip>
</Address>
</Employee>
<Employee>
<EmpId>2</EmpId>
<Name>Lucy</Name>
<Sex>Female</Sex>
<Address>
<Country>USA</Country>
<Zip>95220</Zip>
</Address>
</Employee>
</Employees>
Output I need is
<Employees>
<Employee>
<EmpId>1</EmpId>
<Sex>Male</Sex>
<Address>
<Zip>95220</Zip>
</Address>
</Employee>
<Employee>
<EmpId>2</EmpId>
<Sex>Female</Sex>
<Address>
<Zip>95220</Zip>
</Address>
</Employee>
</Employees>
My code is as below.
private void button1_Click(object sender, EventArgs e)
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load(XML_Path);
// xDoc.Load();
XmlNodeList xNodes = xDoc.SelectNodes("Employees/Employee");
XmlDocument doc = new XmlDocument();
XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
doc.AppendChild(docNode);
XmlNode employeesNode = doc.CreateElement("Employees");
doc.AppendChild(employeesNode);
string s =null;
string keep = "EmpId,Sex,Address/Zip";
string[] strArr = keep.Split(',');
foreach(XmlNode xN in xNodes)
{
XmlNode employeeNode = doc.CreateElement("Employee");
employeesNode.AppendChild(employeeNode);
foreach (string str in strArr)
{
XmlNode xNod = xN.SelectSingleNode(str);
employeeNode.AppendChild(xNod);
}
}
richTextBox1.Text = doc.ToString();
}
I get a weird error in inner foreach loop it wont add the new node can anyone tell me what wrong am i doing.
Thanks
Try this using Linq to Xml, the code will be much succint.
private void button1_Click(object sender, EventArgs e)
{
var xDoc = XDocument.Load(XML_Path)
string keep = "EmpId,Sex,Address,Zip";
string[] strArr = keep.Split(',');
var nodesToDelete = xDoc.Root.Descendants("Employee")
.SelectMany(e => e.Descendants()
.Where(a => !strArr.Contains(a.Name.ToString())));
foreach (var node in nodesToDelete.ToList())
node.Remove();
richTextBox1.Text = xDoc.ToString();
}
How about using XDocument ?
Bellow code gives you the required output.
string xmlString =
#"<?xml version=""1.0""?>
<Employees>
<Employee>
<EmpId>1</EmpId>
<Name>Sam</Name>
<Sex>Male</Sex>
<Address>
<Country>USA</Country>
<Zip>95220</Zip>
</Address>
</Employee>
<Employee>
<EmpId>2</EmpId>
<Name>Lucy</Name>
<Sex>Female</Sex>
<Address>
<Country>USA</Country>
<Zip>95220</Zip>
</Address>
</Employee>
</Employees>
";
System.Xml.Linq.XDocument xmlDoc = System.Xml.Linq.XDocument.Parse(xmlString);
foreach (var employee in xmlDoc.Descendants("Employee"))
{
employee.Descendants("Name").First().Remove();
employee.Descendants("Address").First().Descendants("Country").First().Remove();
}
MessageBox.Show(xmlDoc.ToString());
You could go with something like this:
XmlDocument source = new XmlDocument();
source.LoadXml(xmldata);
var keep = new[] {"EmpId", "Sex", "Address/Zip"};
foreach (XmlElement employee in source.SelectNodes("/Employees/Employee"))
foreach (XmlElement child in employee.SelectNodes(".//*"))
{
if(keep.Any(i => i.StartsWith(child.Name) || i.EndsWith(child.Name)))
continue;
child.ParentNode.RemoveChild(child);
}
Console.WriteLine(source.OuterXml);
However, as your keep array could be changed and different scenarios could be required, you should try go with a XSLT transformation.
I'm trying to load data from the nodes in my xml file to get them to post in a listbox.
Here is what my xml file looks like.
<?xml version="1.0" encoding="utf-8"?>
<MovieData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie>
<Name>Death Race</Name>
<Type>Action</Type>
<Type>Adventure</Type>
<Rating>R</Rating>
<Disk>Blu-Ray</Disk>
</Movie>
<Movie>
<Name>Death Race 2</Name>
<Type>Action</Type>
<Type>Adventure</Type>
<Rating>R</Rating>
<Disk>Blu-Ray</Disk>
</Movie>
</MovieData>
Here is what i am trying to do.
try
{
XmlDocument doc = new XmlDocument();
doc.Load(movieListXML);
XmlNodeList nodeList = doc.SelectNodes("/MovieData");
foreach (XmlNode xn in nodeList)
{
XmlNode movie = xn.SelectSingleNode("Movie");
if (movie != null)
{
movieTypeListBox.Items.Add(movie["Name"].InnerText);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
Can anyone tell me where my problem is ? thanks.
iterate over your Movies not your MovieData
try
{
XmlDocument doc = new XmlDocument();
doc.Load("XMLFile1.xml");
XmlNode node = doc.SelectSingleNode("/MovieData");
foreach (XmlNode movie in node.SelectNodes("Movie"))
{
if (movie != null)
{
movieTypeListBox.Items.Add(movie["Name"].InnerText);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}