Read XML from c# - c#

I'm trying to read a xml file from a c# application. so far no luck at all. This is the XML file
<?xml version="1.0" encoding="utf-8"?>
<ExportJobs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<JobList>
<Job Id="555555">
<Comments></Comments>
<DueDate>2017-11-17</DueDate>
<FormattedDueDate>17-Nov-2017 12:00</FormattedDueDate>
<TargetDueDate>2017-11-17</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Miss Ash</TenantName>
<Uprn>testUpr</Uprn>
<HouseName></HouseName>
</Job>
<Job Id="666666">
<Comments></Comments>
<DueDate>2018-03-15</DueDate>
<FormattedDueDate>15-Mar-2018 12:00</FormattedDueDate>
<TargetDueDate>2018-03-15</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Mr Howard</TenantName>
<Uprn>testUpr2</Uprn>
</Job>
</JobList>
</ExportJobs>
I'm trying to get the job Id and the Uprn from the joblist node and pass the values in to Sql Server DB. I tried this, but I can't get the values,
string costCode;
string uprn;
//File path where the xml is located
string filepath = "C:\\ExportJobs.xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filepath);
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
costCode = node.Attributes["Id"].InnerText;
uprn = node.Attributes["Uprn"].InnerText;
}
I really appreciate any help. Thanks

XmlSerializer is your friend:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
public class ExportJobs
{
public List<Job> JobList { get; } = new List<Job>();
}
public class Job
{
[XmlAttribute]
public int Id { get; set; }
public string Comments { get; set; }
public DateTime DueDate { get; set; }
public string FormattedDueDate { get; set; }
public DateTime TargetDueDate{ get; set; }
public int ServiceTypeId { get; set; }
public string ServiceType { get; set; }
public string TenantName { get; set; }
public string Uprn { get; set; }
public string HouseName { get; set; }
}
static class P
{
static void Main()
{
var ser = new XmlSerializer(typeof(ExportJobs));
ExportJobs jobs;
using (var sr = new StringReader(xml))
{
jobs = (ExportJobs) ser.Deserialize(sr);
}
foreach(var job in jobs.JobList)
{
Console.WriteLine($"{job.Id} / {job.Uprn}: {job.DueDate}");
}
}
const string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<ExportJobs xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<JobList>
<Job Id=""555555"">
<Comments></Comments>
<DueDate>2017-11-17</DueDate>
<FormattedDueDate>17-Nov-2017 12:00</FormattedDueDate>
<TargetDueDate>2017-11-17</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Miss Ash</TenantName>
<Uprn>testUpr</Uprn>
<HouseName></HouseName>
</Job>
<Job Id=""666666"">
<Comments></Comments>
<DueDate>2018-03-15</DueDate>
<FormattedDueDate>15-Mar-2018 12:00</FormattedDueDate>
<TargetDueDate>2018-03-15</TargetDueDate>
<ServiceTypeId>3</ServiceTypeId>
<ServiceType>Service</ServiceType>
<TenantName>Mr Howard</TenantName>
<Uprn>testUpr2</Uprn>
</Job>
</JobList>
</ExportJobs>";
}

You are accessing ChildNodes of root element, which contains only Jobs element, which in order does not contains attributes Id and Uprn.
The usual practice is to use XPath query as following:
foreach (XmlNode node in xmlDoc.DocumentElement.SelectNodes("Jobs/Job"))
{
costCode = node.Attributes["Id"].InnerText;
uprn = node.SelectSingleNode("Uprn").InnerText;
}
Note that Uprn is the node and not the node attribute.

Here is tested code. You need the namespace. See code below which is using xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication67
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement exportJobs = doc.Root;
XNamespace ns = exportJobs.GetDefaultNamespace();
var results = exportJobs.Descendants(ns + "Job").Select(x => new {
id = (string)x.Attribute(ns + "Id"),
comment = (string)x.Element(ns + "Comments"),
dueDate = (DateTime)x.Element(ns + "DueDate"),
formattedDueDate = (DateTime)x.Element(ns + "FormattedDueDate"),
targetDueDate = (DateTime)x.Element(ns + "TargetDueDate"),
serviceTypeId = (int)x.Element(ns + "ServiceTypeId"),
serviceType = (string)x.Element(ns + "ServiceType"),
tenantName = (string)x.Element(ns + "TenantName"),
uprn = (string)x.Element(ns + "Uprn"),
houseName = (string)x.Element(ns + "HouseName")
}).ToList();
}
}
}

I think the best way to solve your problem is XDocument class.
XDocument xDoc = XDocument.Load(#"D:\1.xml");
foreach(var node in xDoc.Descendants("Job"))
{
id = node.Attribute("Id");
foreach(var subnode in node.Descendants("Uprn"))
{
uprn = subnode.Value;
}
//or like that. but check it for null before
uprn = node.Descendants("Uprn")?.First().Value
}

Related

XML - Get Element Value - C#

I am trying to find if an element value exists then get a element value below it. i.e.
<Reply>
<customer id="1223">
<group>A</group>
<class>
<custclass>AB</custclass>
<custval>1</custval>
</class>
<class>
<custclass>CD</custclass>
<custval>2</custval>
</class>
</customer>
</Reply>
I need to get the custval element value if the custclass = "CD". What is the best way to set this into a string in C# "2"? So I am looking for if custclass element value of "CD" exists then return the custval element value of 2.
Thanks for any info.
We can read the property value using various ways-
Method 1 - using XmlDocument
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList xnl = doc.SelectNodes("/Reply/customer/class");
foreach (XmlNode node in xnl)
{
if (node.ChildNodes[0].InnerText == "CD")
{
Console.WriteLine(node.ChildNodes[1].InnerText);
}
}
Method 2 - using XDcoument and LINQ
XDocument xml = XDocument.Load(xmlFile);
var result = xml.Root.DescendantsAndSelf("class")
.Where(r => (string)r.Element("custclass").Value == "CD")
.Select(s=> (string)s.Element("custval").Value).Single();
Console.WriteLine(result);
Method 3 - using XDocument and XPathSelectElement
var custval = xml.XPathSelectElement("Reply/customer/class[custclass='CD']/custval").Value;
Console.WriteLine(custval);
Method 4 - using XmlSerializer
Create C# classes using xmltocsharp & use Deserialize to convert the xml to object
[XmlRoot(ElementName = "class")]
public class Class
{
[XmlElement(ElementName = "custclass")]
public string Custclass { get; set; }
[XmlElement(ElementName = "custval")]
public string Custval { get; set; }
}
[XmlRoot(ElementName = "customer")]
public class Customer
{
[XmlElement(ElementName = "group")]
public string Group { get; set; }
[XmlElement(ElementName = "class")]
public List<Class> Class { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "Reply")]
public class Reply
{
[XmlElement(ElementName = "customer")]
public Customer Customer { get; set; }
}
static async System.Threading.Tasks.Task Main(string[] args)
{
string xmlFile = #"xxxxxx.xml";
using (StreamReader r = new StreamReader(xmlFile))
{
string xmlString = r.ReadToEnd();
XmlSerializer ser = new XmlSerializer(typeof(Reply));
using (TextReader reader = new StringReader(xmlString))
{
var result = (Reply)ser.Deserialize(reader);
var custvalue = result.Customer.Class.Where(i => i.Custclass == "CD").Select(a => a.Custval).Single();
Console.WriteLine(custvalue);
}
}
}

What will be the way to read xml here?

I have this below xml file:-
<?xml version="1.0" encoding="utf-8" ?>
<LoanProduct xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Program>
<ProgramID>6</ProgramID>
<Name>Primary Loan</Name>
<InterestRate>0.23</InterestRate>
<StartDate>2018-12-20</StartDate>
<EndDate>2019-03-31</EndDate>
</Program>
<Program>
<ProgramID>6</ProgramID>
<Name>Primary Loan</Name>
<InterestRate>0.25</InterestRate>
<StartDate>2019-04-1</StartDate>
<EndDate>2099-12-31</EndDate>
</Program>
</LoanProduct>
In my class file I have to read from xml file and run some query on it:-
String xml = Server.MapPath("/Resources/LoanProduct.xml");
DataSet dataSet = new DataSet();
dataSet.ReadXml(xml);
Suppose I want to retrieve where ProgramID= 6 and EndDate = '2099-12-31'
How can I achieve that?
You can achieve your desired result by using XDocument under the namespace System.Xml.Linq.
XDocument doc = XDocument.Load(#"Path to your xml file");
var ns = doc.Root.GetDefaultNamespace();
var result = (from program in doc.Descendants(ns + "Program")
where Convert.ToInt32(program.Element(ns + "ProgramID").Value) == 6 && program.Element(ns + "EndDate").Value == "2099-12-31"
select program).FirstOrDefault();
Output: (From Debugger)
You can use serialization. The date should be consistent otherwise you will get an error with this code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
class Program1
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(LoanProduct));
LoanProduct loanProduct = (LoanProduct)serializer.Deserialize(reader);
}
}
[XmlRoot(ElementName = "LoanProduct", Namespace = "")]
public class LoanProduct
{
[XmlElement("Program", Namespace = "")]
public List<Program> programs { get; set; }
}
[XmlRoot(ElementName = "Program", Namespace = "")]
public class Program
{
public int ProgramID { get; set; }
public string Name { get; set; }
public decimal InterestRate { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
}

Deserialize xml into class having string values in xml

Let me explain, there is a database table which has 1 XML column named audits and other common types of column.
so is this possible to deserialize below XML into class.
<?xml version="1.0"?>
<entity type="Order">
<id type="System.Int64">146</id>
<ordernumber type="System.String">OD555</ordernumber>
<audits type='System.String'>
<audit>
<item>
<create timestamp='2017-07-19 10:02:13' userid='23' />
</item>
<invoice>
<create timestamp='2017-07-19 10:03:37' userid='45' />
</invoice>
</audit>
</audits>
</entity>
Class:
public class Order
{
public long id { get; set; }
public string ordernumber { get; set; }
public string audits { get; set; }
}
Modifying your model with the attributes XmlType and XmlAnyElement (requires XmlElement as type)
[XmlType("entity")]
public class Order
{
public long id { get; set; }
public string ordernumber { get; set; }
[XmlAnyElement]
public XmlElement audits { get; set; }
}
allows to deserialize the complete XML string like
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(xmlString);
writer.Flush();
stream.Position = 0;
XmlSerializer serializer = new XmlSerializer(typeof(Order));
Order o = (Order)serializer.Deserialize(stream);
}
Now you are able to get the audits as string like
string auditsString = o.audits.InnerXml;
You can also add a property to your model to simplify the access:
public string auditsString
{
get
{
return audits.InnerXml;
}
}
Try xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication68
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Order order = doc.Descendants("entity").Select(x => new Order()
{
id = (long)x.Element("id"),
ordernumber = (string)x.Element("ordernumber"),
audits = x.Descendants("create").Select(y => (DateTime)y.Attribute("timestamp")).ToList()
}).FirstOrDefault();
}
}
public class Order
{
public long id { get; set; }
public string ordernumber { get; set; }
public List<DateTime> audits { get; set; }
}
}
You can try like this
const string xmlString = #"<columns><column><c1>100</c1><c2>200</c2><cn>300</cn></column><column><c1>111</c1><c2>222</c2><cn>333</cn></column> <column> <c1>MAX Newsletter</c1><c2>OLS Application</c2> <cn>Total funded accounts</cn> </column></columns>";
XDocument doc = XDocument.Parse(xmlString);
if (doc.Root != null)
{
List<Row> items = (from r in doc.Root.Elements("column")
select new Row
{
C1 = (string)r.Element ("C1"),
C2 = (string)r.Element("C2"),
}).ToList();

How can i filter XmlNodeList

I have a set of data xml node list, i want to filter with a particular attributes inner text, I tried with this but nothing worked. My code here and also posting the xml data
string baseName = categoryName.Split(':').Last();
int categoryId = 0;
string xmlFile = File.ReadAllText(Application.StartupPath + #"\EbayCategories\EbayCategories.xml");
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlFile);
XmlNodeList nodeList = xmldoc.SelectNodes("/CategoryArray/Category[CategoryName='" + baseName + "']");
if (nodeList.Count > 0)
{
var memberNames = nodeList.Cast<XmlNode>().Where(node => node.Attributes["CategoryID"].InnerText == "58193").ToList();
categoryId = int.Parse(nodeList[0]["CategoryID"].InnerText);
}
Here is my xml Data. I want to filter CategoryParentID = My Value.
<CategoryArray>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>20081</CategoryID>
<CategoryLevel>1</CategoryLevel>
<CategoryName>Antiques</CategoryName>
<CategoryParentID>20081</CategoryParentID>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>37903</CategoryID>
<CategoryLevel>2</CategoryLevel>
<CategoryName>Antiquities</CategoryName>
<CategoryParentID>20081</CategoryParentID>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>37908</CategoryID>
<CategoryLevel>3</CategoryLevel>
<CategoryName>The Americas</CategoryName>
<CategoryParentID>37903</CategoryParentID>
<LeafCategory>true</LeafCategory>
</Category>
<Category>
<BestOfferEnabled>true</BestOfferEnabled>
<AutoPayEnabled>true</AutoPayEnabled>
<CategoryID>162922</CategoryID>
<CategoryLevel>3</CategoryLevel>
<CategoryName>Byzantine</CategoryName>
<CategoryParentID>37903</CategoryParentID>
<LeafCategory>true</LeafCategory>
</Category>
I have done with small changes Removed Attributes
var node = nodeList.Cast<XmlNode>().Where(n => n["CategoryParentID"].InnerText == "58193").Select(x => x["CategoryID"].InnerText).SingleOrDefault();
Perfectly worked!!!
Using Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Category.categories = doc.Descendants("Category").Select(x => new Category() {
BestOfferEnabled = (Boolean)x.Element("BestOfferEnabled"),
AutoPayEnabled = (Boolean)x.Element("BestOfferEnabled"),
CategoryID = (int)x.Element("CategoryID"),
CategoryLevel = (int)x.Element("CategoryLevel"),
CategoryName = (string)x.Element("CategoryName"),
CategoryParentID = (int)x.Element("CategoryParentID"),
}).ToList();
int id = 37903;
Category categoryId = Category.categories.Where(x => x.CategoryParentID == id).FirstOrDefault();
}
}
public class Category
{
public static List<Category> categories { get; set; }
public Boolean BestOfferEnabled { get; set; }
public Boolean AutoPayEnabled { get; set; }
public int CategoryID { get; set; }
public int CategoryLevel { get; set; }
public string CategoryName { get; set; }
public int CategoryParentID { get; set; }
}
}

Xml node is getting replaced by another

I want to write university courses in an xml file and be sorted by departments. So if I add a course from different departments I will get something like:
<Departments>
<FST_Department>
<Course_Details>
<Course_Name >Networking</Course_Name>
<Course_Code >xx</Course_Code>
</Course_Details>
</FST_Department >
<Medicine_Department>
<Course_Details>
<Course_Name >General_Medicine</Course_Name>
<Course_Code >xxxxxxx</Course_Code>
</Course_Details>
</Medicine_Department>
</Departments
But instead all my courses go inside the last department I added a course to and the other department 'tags' disapear.Like below.
<Departments>
<Medicine_Department>
<Course_Details>
<Course_Name >Networking</Course_Name>
<Course_Code >xx</Course_Code>
</Course_Details>
<Course_Details>
<Course_Name >General Medicine</Course_Name>
<Course_Code >xxxxxxx</Course_Code>
</Course_Details>
</Medicine_Department>
</Departments
The code to create the xml file .
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Directory.Exists(path = "E:\\Sorting\\Directory"))
Directory.CreateDirectory(path = "E:\\Sorting\\Directory");
if (!File.Exists(path = "E:\\Sorting\\Directory\\Sort.xml"))
{
XmlTextWriter xW = new XmlTextWriter(path ="E:\\Sorting\\Directory\\Sort.xml", Encoding.UTF8);
xW.WriteStartElement("Departments");//my root
xW.WriteEndElement(); // Departments
xW.Close();
This is the code I use to add data to the xml file :
string path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
XmlDocument xDoc = new XmlDocument();
xDoc.Load("E:\\Sorting\\Directory\\Sort.xml");
XmlNode xNode = xDoc.SelectSingleNode("Departments");
xNode.RemoveAll();
if (combboBox.Text == "FST")
{
XmlNode xDepartmentNode = xDoc.CreateElement("FST_Department");
foreach (Course c in courses)
{
XmlNode xCourse_Details = xDoc.CreateElement("Course_Details");
XmlNode xCode = xDoc.CreateElement("Course_Code");
xCode.InnerText = c.courseCode;
xCourse_Details.AppendChild(xCode);
XmlNode xName = xDoc.CreateElement("Course_Name");
xName.InnerText = c.courseName;
xCourse_Details.AppendChild(xName);
xDepartmentNode.AppendChild(xCourse_Details);
xDoc.DocumentElement.AppendChild(xDepartmentNode);
xDoc.Save("E:\\Sorting\\Directory\\Sort.xml");
}
}
else if(combbobox.Text == "Medicine")
{
XmlNode xDepartmentNode = xDoc.CreateElement("Medicine_Department");
foreach (Course c in courses)
{
XmlNode xCourse_Details = xDoc.CreateElement("Course_Details");
XmlNode xCode = xDoc.CreateElement("Course_Code");
xCode.InnerText = c.courseCode;
xCourse_Details.AppendChild(xCode);
XmlNode xName = xDoc.CreateElement("Course_Name");
xName.InnerText = c.courseName;
xCourse_Details.AppendChild(xName);
xDepartmentNode.AppendChild(xCourse_Details);
xDoc.DocumentElement.AppendChild(xDepartmentNode);
xDoc.Save("E:\\Sorting\\Directory\\Sort.xml");
}
}
You clear all departments with this line. So anything that was there before is wiped from the file:
xNode.RemoveAll();
Second, with your foreach on courses, you will add all the courses to that department. Since you neither sort or filter the courses by department, you are getting them all into whatever department combobox.Text is selecting.
Scrap it and use XML Serialization lol
Departments Class
using System.Xml.Serialization;
namespace Serializer
{
public class Departments
{
public FST_Department FST_Department { get; set; }
public Medicine_Department Medicine_Department { get; set; }
}
}
FST_Department Class
using System.Collections.Generic;
namespace Serializer
{
public class FST_Department
{
public List<Course> Course_Details { get; set; }
}
}
Medicine_Department Class
using System.Collections.Generic;
namespace Serializer
{
public class Medicine_Department
{
public List<Course> Course_Details { get; set; }
}
}
Course Class
namespace Serializer
{
public class Course
{
public string Course_Name { get; set; }
public string Course_Code { get; set; }
}
}
Read and write serialized xml
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
namespace Serializer
{
class XMLManager
{
private const string configFileName = "Departments.xml";
public static Departments Read()
{
XmlSerializer reader = new XmlSerializer(typeof(Departments));
using (FileStream file = File.OpenRead(Path.Combine(Global.AppRoot, configFileName)))
{
return reader.Deserialize(file) as Departments;
}
}
public static void Write(Departments settings)
{
XmlSerializer writer = new XmlSerializer(typeof(Departments));
using (FileStream file = File.Create(Path.Combine(Global.AppRoot, configFileName)))
{
writer.Serialize(file, settings);
}
}
}
}
Usage
Departments depart = new Departments();
FST_Department fst = new FST_Department();
fst.Course_Details = new List<Course>();
Course course = new Course();
course.Course_Code = "1";
course.Course_Name = "Test 1";
fst.Course_Details.Add(course);
course = new Course();
course.Course_Code = "2";
course.Course_Name = "Test2";
fst.Course_Details.Add(course);
depart.FST_Department = fst;
Medicine_Department med = new Medicine_Department();
med.Course_Details = new List<Course>();
course = new Course();
course.Course_Code = "3";
course.Course_Name = "Test 3";
med.Course_Details.Add(course);
depart.Medicine_Department = med;
//put it all in some kind of order in case things were added out of order
depart.FST_Department.Course_Details.OrderBy(c => c.Course_Code);
depart.Medicine_Department.Course_Details.OrderBy(c => c.Course_Code);
XMLManager.Write(depart);
XML File Output
<?xml version="1.0"?>
<Departments xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FST_Department>
<Course_Details>
<Course>
<Course_Name>Test 1</Course_Name>
<Course_Code>1</Course_Code>
</Course>
<Course>
<Course_Name>Test2</Course_Name>
<Course_Code>2</Course_Code>
</Course>
</Course_Details>
</FST_Department>
<Medicine_Department>
<Course_Details>
<Course>
<Course_Name>Test 3</Course_Name>
<Course_Code>3</Course_Code>
</Course>
</Course_Details>
</Medicine_Department>
</Departments>

Categories