Select node from XML and Create a new XML - c#

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.

Related

How to extract data from SOAP response in C#

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;

How to update xml file without using Linq?

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);
}

How to group XML nodes based on their values in C#

I have my XML as:
<root>
    <element>
        <id>1</id>
        <group>first</group>
    </element>
    <element>
        <id>2</id>
        <group>second</group>
    </element>
    <element>
        <id>3</id>
        <group>first</group>
    </element>
</root>
Is there anyway we can group the nodes with same values like this:
<root>
    <groups name="first">
<element>
        <id>1</id>
        <group>first</group>
     </element>
<element>
        <id>3</id>
        <group>first</group>
    </element>
</groups>
   <groups name="second"><element>
       <id>2</id>
        <group>second</group>
    </element>
</groups>
</root>
Is there a way to group it based on same node values?
I just tested the code below and it matches your results.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
"<root>" +
"<element>" +
"<id>1</id>" +
"<group>first</group>" +
"</element>" +
"<element>" +
"<id>2</id>" +
"<group>second</group>" +
"</element>" +
"<element>" +
"<id>3</id>" +
"<group>first</group>" +
"</element>" +
"</root>";
XDocument doc = XDocument.Parse(xml);
var groups = doc.Descendants("element")
.GroupBy(x => (string)x.Element("group"))
.ToList();
XElement newXml = new XElement("root");
foreach(var group in groups)
{
newXml.Add(new XElement("groups", new object[] {
new XAttribute("name", group.Key),
group
}));
}
}
}
}
XPath approach
string val= "first";
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("./Xml/YourXML.xml"));
string text = string.Empty;
XmlNodeList xnl = doc.SelectNodes("/root/groups ");
foreach (XmlNode node in xnl)
{
text = node.Attributes["name"].InnerText;
if (text == val)
{
XmlNodeList xnl = doc.SelectNodes(string.Format("/root/groups [#name='{0}']/element", val));
foreach (XmlNode node2 in xnl )
{
text = text + "<br>" + node2["id"].InnerText;
text = text + "<br>" + node2["group"].InnerText;
}
}
Response.Write(text);
}
or
var nodes = (from n in xml.Descendants("element").
Where(r => r.Parent.Attribute("name").Value == "first")
select new
{
id = (string)n.Element("id").Value,
group = (string)n.Element("group").Value
}).ToList();
Group the elements and build up a new document placing each group in a new <groups> element.
var newDoc = new XDocument(
new XElement("root",
from e in doc.Descendants("element")
group e by (string)e.Element("group") into g
select new XElement("groups",
new XAttribute("name", g.Key),
g
)
)
);
I had to try this in VB. My Group By skills need a lot of practice.
Using this test data
Dim myXML As XElement
myXML = <root>
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>2</id>
<group>second</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
</root>
this seems to work
Dim newXML As XElement = <root></root>
newXML.Add(From el In myXML.Elements
Order By el.<group>.Value
Group By gn = el.<group>.Value Into g = Group
Select New XElement("Groups", New XAttribute("name", gn), g))
newXML =
<root>
<Groups name="first">
<element>
<id>1</id>
<group>first</group>
</element>
<element>
<id>3</id>
<group>first</group>
</element>
</Groups>
<Groups name="second">
<element>
<id>2</id>
<group>second</group>
</element>
</Groups>
</root>
Similar to other answers.

reading xml doc into tree view c#

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

How to check a string exists in a xml node's value asp.net

My xml will be like below
<Employee>
<Emp>
<Name id="1" link="/office1/manager"></Name>
<Name id="2" link="/office/sweeper"></Name>
<Name id="3" link="/office2/manager"></Name>
</Emp>
</Employee>
I want to get the "id" of the employees who contains string "manager" in "link"
Using linq to xml:
XDocument doc = XDocument.Load("XMLFilePath");
var selectors = from elements in doc.Elements("Employee").Elements("Emp").Elements("Name")
where elements.Attribute("link").Value.Contains("manager")
select elements;
string ids = string.Empty;
foreach (var element in selectors)
{
ids += element.Attribute("id").Value + ",";
}
Also, for loading from string you can use:
XDocument doc = XDocument.Parse(xmlString);
var xDoc = XDocument.Parse(xml); //XDocument.Load(filename)
var ids = xDoc.Descendants("Name")
.Where(n => n.Attribute("link").Value.Contains("/manager"))
.Select(n => n.Attribute("id").Value)
.ToList();
//SELECT THE VALUES FROM XML USING C#
<?xml version="1.0" encoding="iso-8859-1"?>
<CONFIG>
<UsersList>
<SystemName>DOTNET-PC</SystemName>
<UserName>KARTHIKEYAN</UserName>
<ImagePath>C:\Users\DEVELOPER\AppData\Roaming\Office Messenger\assets\insta.jpg</ImagePath>
<PhotoPath>C:\Users\DEVELOPER\AppData\Roaming\Office Messenger\assets\NoPhoto.png</PhotoPath>
<UserStatus>Available</UserStatus>
<CustomStatus>Available</CustomStatus>
<theme>FF8B0000</theme>
</UsersList>
</CONFIG> //XML DOCUMENT
//C#
DataSet ds = new DataSet();
try
{
ds.ReadXml("C:\\config.xml");
}
catch { };
if (ds.Tables.Count > 0)
{
var results = from myRow in ds.Tables[0].AsEnumerable() where myRow.Field<string> ("SystemName") == SystemInformation.ComputerName select myRow;//ds.Tables[0] is <CONFIG> tag //in where SystemName=My system name to select the values from xml
foreach (var cust in results)
{
string _myName = cust["UserName"].ToString();
string _myLogoPath = cust["ImagePath"].ToString();
string _customStatus = cust["CustomStatus"].ToString();
string _myPhotoPath = cust["PhotoPath"].ToString();
}
}
//CREATE XML FROM C#
XDocument xmlDoc = XDocument.Load("C:\\config.xml");
xmlDoc.Root.Add(new XElement("UsersList", new XElement("SystemName", SystemInformation.ComputerName), new XElement("UserName", SystemInformation.ComputerName), new XElement("ImagePath", _filesPath + "\\insta.jpg"), new XElement("PhotoPath", _filesPath + "\\NoPhoto.png"), new XElement("UserStatus", "Available"), new XElement("CustomStatus", "Available"), new XElement("theme", "000000")));
xmlDoc.Save("C:\\config.xml");

Categories