I have some XML file sitting in my /bin folder (using vs2010). I would like to extract some data (attributes, element values) from this xml. What is the best way to do this using C#?
do I need to use XMLTextReader or would it be better to create an xmlDocument...I'm confused...
You could use LINQ to XML ?
http://msdn.microsoft.com/en-us/library/bb387098.aspx
Either System.Xml og System.Linq.Xml
I would recommend Linq2Xml: http://msdn.microsoft.com/en-us/library/bb387098.aspx
This is a good guide: http://www.hookedonlinq.com/LINQtoXML5MinuteOverview.ashx
Here is a simple example of querying an XML File
public class Job
{
public int Id { get; set; }
public int CompanyId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int HoursPerWeek { get; set; }
}
Snippet of XML File:
<?xml version="1.0" encoding="utf-8"?>
<Jobs>
<Job>
<Id>1</Id>
<CompanyId>2</CompanyId>
<Description>Must be willing to relocate to Nebraska.</Description>
<HoursPerWeek>90</HoursPerWeek>
<JobStatus>1</JobStatus>
<JobType>3</JobType>
<Location>NY</Location>
<Title>Application Engineer for Gooooogle Plus</Title>
</Job>
<Job>
Class which populates Job objects from XML File
public class XMLJobsDAL
{
XDocument JobDoc
{
get { return XDocument.Load(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/Job.xml")); }
}
public List<Job> GetJobs()
{
var results = from job in JobDoc.Descendants("Job")
select new Job
{
Id = (int)job.Element("Id"),
CompanyId = (int)job.Element("CompanyId"),
Description = (string)job.Element("Description"),
HoursPerWeek = (int)job.Element("HoursPerWeek"),
Title = (string) job.Element("Title")
}
return results.ToList();
}
public Job GetJob(int id)
{
var result = from job in JobDoc.Descendants("Job")
where (int)job.Element("Id") == id
select new Job
{
Id = (int)job.Element("Id"),
CompanyId = (int)job.Element("CompanyId"),
Description = (string)job.Element("Description"),
HoursPerWeek = (int)job.Element("HoursPerWeek"),
Title = (string) job.Element("Title")
}
return result.SingleOrDefault();
}
}
I would recommend that you check Serialization out. Here is a little example of how to Serialize/Deserialize XML to/from an object using ek_ny XML Document:
using System.IO;
using System.Xml.Serialization;
[Serializable]
public class Job
{
public Job() { }
public int ID { get; set; }
public int CompanyID { get; set; }
public string Description { get; set; }
public int HoursPerWeek { get; set; }
public int JobStatus { get; set; }
public int JobType { get; set; }
public string Location { get; set; }
public string Title { get; set; }
public void SerializeToXML(string path)
{
//creates a file
FileStream fs = new FileStream(path, FileMode.Create);
//now we create the serializer
XmlSerializer xs = new XmlSerializer(typeof(Job));
//serialize it to the file
xs.Serialize(fs, this);
//close the file
fs.Close();
}
public static Job DeserializeToJob(string path) {
//open file
FileStream fs = new FileStream(path, FileMode.Open);
//create deserializer
XmlSerializer xs = new XmlSerializer(typeof(Job));
//Deserialize
Job job = (Job)xs.Deserialize(fs);
//close the file
fs.Close();
//return the deserialized job
return job;
}
}
Implementation:
class Program
{
static void Main(string[] args)
{
Job j = new Job();
j.CompanyID = 2;
j.ID = 1;
j.Description = "Must be willing to relocate to Nebraska.";
j.HoursPerWeek = 90;
j.JobStatus = 1;
j.JobType = 3;
j.Location = "NY";
j.Title = "Application Engineer for Gooooogle Plus";
//Saving the object serialized.
j.SerializeToXML("MyJob.xml");
//deserialize the saved job into a new instance
Job j2 = Job.DeserializeToJob("MyJob.xml");
}
}
With this you will get the object back and forth from and to XML. This is what has worked the best for me. The only cons I can see here is that if your XML document has a lot of properties you will have to make a very big class.
http://support.microsoft.com/kb/815813
http://www.switchonthecode.com/tutorials/csharp-tutorial-xml-serialization
Good luck!
There's one good way to do this. You can use
DataTable.ReadXml(string fileName)
it takes care of everything easily. Depending on the problem, it could be useful or not.
Note this methods cannot get the schema from xml file, you should add columns to the table yourself.
Related
I'm working on a project that requires that I take data from an XML API that I don't control and insert that data into a database (I control) for consumption by a different application (I control but can't modify code too it)
We already managed to get the full database layout from the XML API via the providers special endpoint but all other queries (to get data) are via XML.
Currently what I have is the following:
Book.cs - This represents one of the database tables
using System;
using System.ComponentModel.DataAnnotations;
namespace Example.Models
{
public partial class Book
{
[Key]
public int Recordno { get; set; }
public decimal? Amount { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? Whenmodified { get; set; }
public DateTime? Whencreated { get; set; }
public int? Createdby { get; set; }
public int? Modifiedby { get; set; }
}
}
API Return XML - This represents the XML returned by the API (an SDK already converts it to an XMLDocument Type)
<BOOKS>
<BOOK>
<RECORDNO>1</RECORDNO>
<AMOUNT>24.12</AMOUNT>
<TITLE>This is a title</TITLE>
<DESCRIPTION>This is a description</DESCRIPTION>
<WHENMODIFIED></WHENMODIFIED>
<WHENCREATED>13/03/20 15:23:12</WHENCREATED>
<CREATEDBY>3</CREATEDBY>
<MODIFIEDBY></MODIFIEDBY>
</BOOK>
<BOOK>
<RECORDNO>1</RECORDNO>
<AMOUNT>24.12</AMOUNT>
<TITLE>This is another title</TITLE>
<DESCRIPTION>This is another description</DESCRIPTION>
<WHENMODIFIED>21/03/20 12:45:23</WHENMODIFIED>
<WHENCREATED>15/03/20 15:23:12</WHENCREATED>
<CREATEDBY>6</CREATEDBY>
<MODIFIEDBY>2</MODIFIEDBY>
</BOOK>
</BOOKS>
Currently the way we are doing this is via switch statements, but I'm hoping that there is a better method.
Partial BookController.cs
var books = new List<Book>();
// response.Data return is a List<XElement>
foreach (var result in response.Data)
{
var xElements = result.Elements();
var book = new Book();
foreach (var element in xElements)
{
switch (element.Name.ToString())
{
case "RECORDNO":
book.Recordno = int.Parse(element.Value);
break;
case "AMOUNT":
if (element.Value != string.Empty)
{
book.Amount = int.Parse(element.Value);
}
break;
// etc.
}
}
books.Add(book);
}
I have a feeling that there is a much easier method of doing this that I'm just unaware of, my concern is that I have about fifty tables and hundreds of elements to do making this method inefficient time wise.
Use xml serialization
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication8
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Books));
Books books = (Books)serializer.Deserialize(reader);
}
}
[XmlRoot("BOOKS")]
public class Books
{
[XmlElement("BOOK")]
public List<Book> books { get; set; }
}
public partial class Book
{
//[Key]
[XmlElement("RECORDNO")]
public int Recordno { get; set; }
[XmlElement("AMOUNT")]
public decimal? Amount { get; set; }
[XmlElement("TITLE")]
public string Title { get; set; }
[XmlElement("DESCRIPTION")]
public string Description { get; set; }
private DateTime Whenmodified { get;set;}
[XmlElement("WHENMODIFIED")]
public string _Whenmodified {
get { return Whenmodified.ToString("dd/MM/yy HH:mm:ss"); }
set { DateTime.ParseExact(value,"dd/MM/yy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);}
}
private DateTime Whencreated { get; set; }
public string _Whencreated
{
get { return Whencreated.ToString("dd/MM/yy HH:mm:ss"); }
set { DateTime.ParseExact(value, "dd/MM/yy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); }
}
[XmlElement("CREATEDBY")]
public int? Createdby { get; set; }
[XmlElement("MODIFIEDBY")]
public int? Modifiedby { get; set; }
}
}
You could do the following:
use XML Schema Definition Tool (Xsd.exe) to generate classes
use generated classes to read the input XML
use that data
Welcome to SO 👋
What you're trying to do here of construct an in-memory C# object from an XML string representation is known as deserialization. There are lots of resources on this (e.g. MSDN), but here's a link to a SO post where the OP was looking to achieve something similar.
Applied to your above code snippet might look something like this:
var books = new List<Book>();
var serializer = new XmlSerializer(typeof(Book));
// response.Data return is a List<XElement>
foreach (var result in response.Data)
{
var book = (Book)serializer.Deserialize(result.CreateReader());
books.Add(book);
}
I have the following XML;
<?xml version="1.0" encoding="UTF-8" ?>
<feedback>
<report_metadata>
<org_name>example.com</org_name>
</report_metadata>
</feedback>
and the following Feedback.cs class;
[XmlRoot("feedback", Namespace = "", IsNullable = false)]
public class Feedback
{
[XmlElement("report_metadata")]
public MetaData MetaData { get; set; }
}
[XmlType("report_metadata")]
public class MetaData
{
[XmlAttribute("org_name")]
public string Organisation { get; set; }
}
When I attempt to deserialize, the value for Organisation is null.
var xml = System.IO.File.ReadAllText("example.xml");
var serializer = new XmlSerializer(typeof(Feedback));
using (var reader = new StringReader(input))
{
var feedback = (Feedback)serializer.Deserialize(reader);
}
Yet, when I change Feedback.cs to the following, it works (obviously the property name has changed).
[XmlType("report_metadata")]
public class MetaData
{
//[XmlAttribute("org_name")]
public string org_name { get; set; }
}
I want the property to be Organisation, not org_name.
In the example XML file org_name is an XML element, not an XML attribute. Changing
[XmlAttribute("org_name")] to [XmlElement("org_name")] at the Organisation property will deserialize it as an element:
[XmlElement("org_name")]
public string Organisation { get; set; }
probably just typo
[XmlAttribute("org_name")]
public string Organisation { get; set; }
was supposed to be
[XmlElement("org_name")]
public string Organisation { get; set; }
Try to modify your Xml classes like
[XmlRoot(ElementName = "report_metadata")]
public class MetaData
{
[XmlElement(ElementName = "org_name")]
public string Organisation { get; set; }
}
[XmlRoot(ElementName = "feedback")]
public class Feedback
{
[XmlElement(ElementName = "report_metadata")]
public MetaData MetaData { get; set; }
}
Then you will get your desired output like
class Program
{
static void Main(string[] args)
{
Feedback feedback = new Feedback();
var xml = System.IO.File.ReadAllText(#"C:\Users\Nullplex6\source\repos\ConsoleApp4\ConsoleApp4\Files\XMLFile1.xml");
var serializer = new XmlSerializer(typeof(Feedback));
using (var reader = new StringReader(xml))
{
feedback = (Feedback)serializer.Deserialize(reader);
}
Console.WriteLine($"Organization: {feedback.MetaData.Organisation}");
Console.ReadLine();
}
}
Output:
This is my first question on SO, please let me know if I am doing anything wrong!
I am trying to parse an XML similar to this:
<LiveUpdate>
<CityID>F0A21EA2</CityID>
<CityName>CityTown</CityName>
<UserName>john</UserName>
<ApplicationDetails>
<ApplicationDetail
Application="AC"
Licensed="true"
Version="2015.2"
Patch="0001"
/>
<ApplicationDetail
Application="AP"
Licensed="true"
Version="2015.2"
Patch="0002"
/>
</ApplicationDetails>
</LiveUpdate>
I have classes that look like this:
public class Client
{
public string cityID { get; set; }
public string cityName { get; set; }
public string userName { get; set; }
public List<Apps> appList { get; set; }
}
public class Apps
{
public string app { get; set; }
public string licensed { get; set; }
public string version { get; set; }
public string patch { get; set; }
}
I need to be able to have a client class with a list of all the application details to be iterated over.
So far the best I've come up with is:
XDocument xml = XDocument.Load(#"C:\blah\Desktop\1.xml");
var liveUpdate = xml.Root;
var clients = (from e in liveUpdate.Elements()
select new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
appList = e.Elements("ApplicationDetails")
.Select(a => new Apps()
{
app = a.Element("Application").Value,
licensed = a.Element("Licensed").Value,
version = a.Element("Version").Value,
patch = a.Element("Patch").Value
}).ToList()
});
However, I'm currently running into an error that says Object reference not set to an instance of an object.
I've seen some similar examples on here, but not that deal with data before the multiple children.
I'm fairly new to XML and Linq so any help here would be greatly appreciated!
Your XML only contains one LiveUpdate tag, so rather than iterating over all of the elements inside of it, you just want to look at the Root element.
In ApplicationDetails, Application, Licensed and such are attributes, not elements. Use .Attribute() to access them.
ApplicationDetails is a single tag, and inside it you have ApplicationDetail tags.
There is no DateTime element in your LiveUpdate tag.
This works:
var liveUpdate = xml.Root;
var e = liveUpdate;
var clients = new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
//dateTime = e.Element("DateTime").Value,
appList = e.Element("ApplicationDetails").Elements("ApplicationDetail")
.Select(a => new Apps()
{
app = a.Attribute("Application").Value,
licensed = a.Attribute("Licensed").Value,
version = a.Attribute("Version").Value,
patch = a.Attribute("Patch").Value
}).ToList()
};
Since you have already defined a class into which you wish to deserialize, you can use XmlSerializer to deserialize it for you.
First, let's rename some of your property names to more closely match the XML and c# naming conventions:
[XmlRoot("LiveUpdate")]
public class Client
{
public string CityID { get; set; }
public string CityName { get; set; }
public string UserName { get; set; }
[XmlArray("ApplicationDetails")]
[XmlArrayItem("ApplicationDetail")]
public List<Apps> AppList { get; set; }
}
public class Apps
{
[XmlAttribute]
public string Application { get; set; }
[XmlAttribute]
public bool Licensed { get; set; }
[XmlAttribute]
public string Version { get; set; }
[XmlAttribute]
public string Patch { get; set; }
}
Then add the following extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
object result = new XmlSerializer(typeof(T)).Deserialize(reader);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
public static T LoadFromFile<T>(string filename)
{
using (var fs = new FileStream(filename, FileMode.Open))
{
object result = new XmlSerializer(typeof(T)).Deserialize(fs);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
}
Now you can deserialize from your XML file as follows:
string fileName = #"C:\blah\Desktop\1.xml";
var client = XmlSerializationHelper.LoadFromFile<Client>(fileName);
I manually updated your Client class to map correctly to the provided XML, but if you wanted to do it automatically, see here: Generate C# class from XML.
I have an *.XMl file like this :
<?xml version="1.0" encoding="utf-8" ?>
- <!-- User settings
-->
- <Settings>
<Session_File>C:\programs\Notepad++Portable\help.html</Session_File>
<Training_catalogue>C:\Windows\Cursors\aero_ew.cur</Training_catalogue>
<Users_ID>C:\Windows\_default.pif</Users_ID>
<File_with_badge_ID>C:\Windows\PFRO.log</File_with_badge_ID>
<Session_Folder>C:\Program Files</Session_Folder>
<PDF_Folder>C:\Program Files\GRETECH\GomPlayer\logos</PDF_Folder>
</Settings>
I would like put each "path" to a variable.
For example "String user_id = C:\Windows_default.pif" I have a following code to read an XML file.
//Read values from xml file
XElement xelement = XElement.Load("settings.xml");
IEnumerable<XElement> employees = xelement.Elements();
// Read the entire XML
foreach (var employee in employees)
{
Maybe in this place I have to write some code
}
Please help me
You could use a Dictionary<string,string> in this case which keys would be element names and values are paths:
var settings = XDocument.Load("settings.xml").Root
.Elements()
.ToDictionary(x => x.Name, x => (string)x);
Then you can access each path by it's element name.For example: settings["Users_ID"] will return C:\Windows\_default.pif
If those are static fields in the XML, you can serialize to a class using DataContractSerializer (or XMLSerializer)...
using System.Runtime.Serialization;
using System.IO;
(also need to add the reference to System.Runtime.Serialization)
[DataContract]
public class Settings
{
[DataMember]
public string Session_File { get; set; }
[DataMember]
public string Training_catalogue { get; set; }
[DataMember]
public string Users_ID { get; set; }
[DataMember]
public string File_with_badge_ID { get; set; }
[DataMember]
public string Session_Folder { get; set; }
[DataMember]
public string PDF_Folder { get; set; }
public static Settings ReadSettings(string Filename)
{
using (var stream = new FileStream(Filename, FileMode.OpenOrCreate))
try
{
return new DataContractSerializer(typeof(Settings)).ReadObject(stream) as Settings;
}
catch { return new Settings(); }
}
public void Save(string Filename)
{
using (var stream = new FileStream(Filename, FileMode.Create, FileAccess.Write))
new DataContractSerializer(typeof(Settings)).WriteObject(stream, this);
}
public Settings()
{
//defaults
}
}
I have an API which creates a sample xml based on a schema file. Here is the link to the API: http://msdn.microsoft.com/en-us/library/aa302296.aspx
Now, suppose the sample xml generated is as follows:
<Employee>
<Name>Bond</Name>
<Address>abc,unknown street</Address>
<phone>0000000000</phone>
<Employee>
I have a employee DB table consisting of thousands of employee records. What I want is to write those records in the xml format created above (table has many non-required columns also so can't use dataset.writexml).
What is the right approach of doing this ? Should I edit the first node as per the first record and then copy the whole node into same xml, write the record then repeat the process till end of records? Or is there a better approach of doing this?
Here is an example of serializing and Deserializing Objects to xml.
using System;
using System.Xml.Serialization;
using System.IO;
namespace Test
{
[Serializable]
[XmlRoot("DocumentElement")]
public class Documentelement
{
[XmlElement]
public PartInfo[] PartInfo { get; set; }
}
public class PartInfo
{
[XmlElement]
public int ID { get; set; }
public string Name { get; set; }
public string PartNo { get; set; }
public int SerialNo { get; set; }
public string Parameter { get; set; }
public DateTime InstallDate { get; set; }
public DateTime InstallTill { get; set; }
}
public class Test
{
private PartInfo details_1()
{
PartInfo details = new PartInfo
{
ID = 0,
Name = "QVR",
PartNo = "A11",
SerialNo = 453,
Parameter = "C -11",
// This you should add as date time, I just used the string to parse your time that you showed in your example.
InstallDate = DateTime.Parse("2013-02-04T17:16:56.383+05:30"),
InstallTill = DateTime.Parse("2013-02-15T17:16:56.3830837+05:30")
};
return details;
}
private PartInfo details_2()
{
PartInfo details = new PartInfo
{
ID = 1,
Name = "EAFR",
PartNo = "B07",
SerialNo = 32,
Parameter = "B-16",
// This you should add as date time, I just used the string to parse your time that you showed in your example.
InstallDate = DateTime.Parse("2013-02-18T17:17:44.589+05:30"),
InstallTill = DateTime.Parse("2013-02-28T17:17:44.589+05:30")
};
return details;
}
public void setXmlValues()
{
Documentelement testOut = new Documentelement { PartInfo = new[] { details_1(), details_2() }};
xml_serialise(testOut);
Documentelement testIn = xml_deserialise();
int val = testIn.PartInfo[0].ID;
DateTime dt = testIn.PartInfo[0].InstallDate;
string shortTime = dt.ToShortTimeString();
}
private void xml_serialise(Documentelement test)
{
XmlSerializer ser = new XmlSerializer(typeof(Documentelement));
using (TextWriter writer = new StreamWriter("test.xml"))
{
ser.Serialize(writer, test);
}
}
private Documentelement xml_deserialise()
{
XmlSerializer ser = new XmlSerializer(typeof(Documentelement));
Documentelement test;
using (TextReader writer = new StreamReader("test.xml"))
{
test = (Documentelement)ser.Deserialize(writer);
}
return test;
}
}
}
One approach could be to use xsd.exe (one of the VS command-line tools) to generate a schema from the sample XML, and then use xsd.exe with the schema to generate a C# class. This class is ready to use with serialization, so you could convert your data into a List<GeneratedClass> and then serialize it. Not saying it's the best approach, but I've used it before successfully.