Xml attendance: output attendance of selected student c# - c#

I'm having a little trouble. This is what my xml look like:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Students xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="student.xsd">
<Student>
<StudentID>001</StudentID>
<FirstN>John</FirstN>
<LastN>Doe</LastN>
<Seat>A1</Seat>
<PresentOn>10/7/2013 8:55:52 AM</PresentOn>
<PresentOn>10/7/2013 8:55:52 AM</PresentOn>
<PresentOn>10/7/2013 8:55:52 AM</PresentOn>
</Student>
<Student>
<StudentID>001</StudentID>
<FirstN>Jane</FirstN>
<LastN>Doe</LastN>
<Seat>A2</Seat>
<PresentOn>10/7/2013 8:55:52 AM</PresentOn>
<PresentOn>10/7/2013 8:55:52 AM</PresentOn>
<PresentOn>10/7/2013 8:55:52 AM</PresentOn>
</Student>
</Students>
I'm having no problem going to the specific student, say Jane Doe, but when I try to output the PresentOn dates, only the first one shows up on the richtextbox. I've set the PresentOn element on my .xsd to be able to occur multiple times if that's any help.
How would I go about outputting all her PresentOn dates and not just one, I'm using XmlNodelist the foreach(XmlNode node in XmlNodelist) and to find the student and I'm able to output the first date, but have no idea how to output PresentOn more than once.
Here's the code I'm using to find the student:
XmlDocument students = new XmlDocument();
students.Load(#"C:\SeatingChart\StudentCopy.xml");
XmlNodeList studentID = students.SelectNodes("Students/Student");
//user chooses student id from combobox
string stuID = removeTextBoxSpace(comboBox1.GetItemText(comboBox1.SelectedItem));
foreach (XmlNode node in studentID)
{
try
{
string xmlID = node["StudentID"].InnerText.ToString();
if (stuID.Equals(xmlID))
{
//Messagebox showing the students name to verify correct student
string preson = node["PresentOn"].InnerText.ToString();
richTextBox1.AppendText("Present on: " + preson.ToString() + "\n");
}
That's all I have so far, new at this and still so much to learn.

Related

Dynamically Creating LINQ Query to query XML Elements

I have an XML similar to below,
<?xml version="1.0" encoding="utf-8"?>
<body>
<Result>
<Students>
<Student>
<Firstname Value="Max" />
<LastName Value="Dean" />
</Student>
<Student>
<Firstname Value="Driz" />
<LastName Value="Jay" />
</Student>
</Students>
</Result>
</body>
I'm trying to figure out if its possible to write some code to dynamically build a Linq query from something like,
string pathToElements = "Result.Students.Student";
var individualItems = pathToElements.Split(".");
to something like,
XElement document = XElement.Parse(xml);
var evaluatedResult = document.Elements("Result")
.Elements("Students")
.Elements("Student")
.ToList();
basically the individualItems has 3 items "Result", "Students" and "Student". I'm exploring possible ways to create a dynamic query to accomplish something like,
document.Elements("Result")
.Elements("Students")
.Elements("Student")
.ToList();
For n number of Items I would just need to keep appending .Elements("") to the query.
Any advice on how to achieve this, my first instinct was to use a string builder and have the query as a string. I was able to do this but was not able to find out how a LINQ query which is in the form of a string can be executed.
Thanks in advance
You can query XML using XPath (it's a string so you can create it dynamically) then you can deserialize the item
https://learn.microsoft.com/en-us/troubleshoot/developer/dotnet/framework/general/query-xpathdocument-xpath-csharp
The output of this code
using System;
using System.Xml.XPath;
using System.IO;
public class Program
{
public static void Main()
{
var xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<body>
<Result>
<Students>
<Student>
<Firstname Value=""Max"" />
<LastName Value=""Dean"" />
</Student>
<Student>
<Firstname Value=""Driz"" />
<LastName Value=""Jay"" />
</Student>
</Students>
</Result>
</body>";
using (TextReader tr = new StringReader(xml))
{
var xdoc = new XPathDocument(tr);
var nav = xdoc.CreateNavigator();
var nodeIter = nav.Select("/body/Result/Students/Student");
while (nodeIter.MoveNext())
{
Console.WriteLine("Student: {0}", nodeIter.Current.InnerXml);
};
}
}
}
is
Student: <Firstname Value="Max" />
<LastName Value="Dean" />
Student: <Firstname Value="Driz" />
<LastName Value="Jay" />

Search between two dates and populate datagrid with dates in between from xml file

so I'm kind of stuck, I want to create a form which allows the user to select a date and then pick a second date and the system will populate the DataGridView with all the event dates in between the two dates. I've looked at tutorials online but they all use sql and not xml.
The perfect scenario should be a user picks date1 and then picks date2, the system retrieves the dates from xml which are in between date1 to date2 and populates the information on the DataGridView.
can you please give a code for example as well.
First, you need get the result that between date1 and date2.
Second, you need create a dynamic datatable according the result.
Finally, you could show the correspond data in the datagirdview.
The following is a code example.
private void button1_Click(object sender, EventArgs e)
{
DateTime formatter = dateTimePicker1.Value;
DateTime latter = dateTimePicker2.Value;
XDocument doc = XDocument.Load("D:\\t.xml");
var result = (from x in doc.Descendants("Student")
where Convert.ToDateTime(x.Element("Date").Value) <= latter && Convert.ToDateTime(x.Element("Date").Value) >= formatter
select x).ToList();
List<string> column = new List<string>();
foreach (var item in result.Elements())
{
column.Add(item.Name.ToString());
}
column=column.Distinct().ToList();
DataTable table = new DataTable();
foreach (var item in column)
{
table.Columns.Add(item);
}
foreach (var item in result)
{
table.Rows.Add(item.Element("Name").Value, item.Element("Date").Value, item.Element("Age").Value);
}
dataGridView1.DataSource = table;
}
Xml file:
<?xml version="1.0" encoding="UTF-8"?>
<School>
<Student>
<Name>test1</Name>
<Date>2019-01-02</Date>
<Age>21</Age>
</Student>
<Student>
<Name>test2</Name>
<Date>2019-03-01</Date>
<Age>22</Age>
</Student>
<Student>
<Name>test3</Name>
<Date>2019-07-01</Date>
<Age>22</Age>
</Student>
<Student>
<Name>test4</Name>
<Date>2019-10-05</Date>
<Age>23</Age>
</Student>
<Student>
<Name>test5</Name>
<Date>2019-11-01</Date>
<Age>24</Age>
</Student>
</School>
Result:

C# Add nodes in xml

I am trying to add my code for already existing code. I have below two xmls:
Out.xml
<School>
<Classes>
<Class>
<Students>
<Student SequenceNumber="1">
<ID>123</ID>
<Name>AAA</Name>
</Student>
</Students>
</Class>
</Classes>
</School>
In.xml
<School>
<Classes>
<Class>
<Students>
<Student SequenceNumber="1">
<ID>456</ID>
<Name>BBB</Name>
</Student>
<Student SequenceNumber="2">
<ID>123</ID>
<Name>AAA</Name>
</Student>
<Student SequenceNumber="3">
<ID>789</ID>
<Name>CCC</Name>
</Student>
</Students>
</Class>
</Classes>
</School>
Now I need to check Out.xml and In.xml and my final Out.xml must be like below. The rule here is check StudentID in Out and In xmls. If Out xml doesnot have it and In xml has it add it to Out.xml at end of already existing elements.
Out.xml
<School>
<Classes>
<Class>
<Students>
<Student SequenceNumber="1">
<ID>123</ID>
<Name>AAA</Name>
</Student>
<Student SequenceNumber="2">
<ID>456</ID>
<Name>BBB</Name>
</Student>
<Student SequenceNumber="3">
<ID>789</ID>
<Name>CCC</Name>
</Student>
</Students>
</Class>
</Classes>
</School>
Already existing code is as below
string inFileName = #"C:\In.xml";
string inXml = System.IO.File.ReadAllText(inFileName);
var xmlReaderSource = XmlReader.Create(new StringReader(inXml));
var mgr = new XmlNamespaceManager(xmlReaderSource.NameTable);
mgr.AddNamespace("m", "http://www.mismo.org/residential/2009/schemas");
XDocument sourceXmlDoc = XDocument.Load(xmlReaderSource);
string outFileName = #"C:\Out.xml";
string outXml = System.IO.File.ReadAllText(outFileName);
XmlDocument targetXmlDoc = new XmlDocument();
targetXmlDoc.LoadXml(outXml);
I cannot change above code now I need add my logic.
I added like below
string xpath = #"/m:School/m:Classes/m:Class/m:Students";
XmlNodeList outStudentNodes = targetXmlDoc.SelectNodes(xpath + "/m:Student", namespaceManager);
if(outStudentNodes== null || outStudentNodes.Count <= 0)
{
return;
}
XElement root = sourceXmlDoc.Root;
IEnumerable<XElement> inStudentsColl = from item in root.Elements("Classes").Descendants("Class")
.Descendants("Students").Descendants("Student")
select item;
Now I have XmlNodeList and IEnumerble, trying to see whether I can use LINQ statement and make code simple for my comparison.
Node: I am not asking how to add nodes/elements using C#. I am looking for how to compare two xmls and then add nodes/elements into the one which is missing those nodes/elements. My issue here is one xml is read like XDocument and other using XmlDocument.
UPDATE
Thank you very much #TheAnatheme. I really appreciate it.
I followed what TheAnatheme suggested me and it worked. I marked TheAnatheme's answer as real solution. Please see below what I did in foreach block so that if anyone wants to use they can refer to this post.
string xpath = #"/m:School/m:Classes/m:Class/m:Students
XmlNode studentsNode = targetXmlDoc.SelectSingleNode(xpath, namespaceManager);
foreach (var element in elementsToAdd)
{
//Add Microsoft.CSharp.dll (if needed ) to your project for below statement to work
dynamic studentElement = element as dynamic;
if (studentElement != null)
{
XmlElement studentXmlElement = targetXmlDoc.CreateElement("Student");
XmlElement studentIDXmlElement = targetXmlDoc.CreateElement("ID");
studentIDXmlElement.InnerText = studentElement.ID;
XmlElement studentNameXmlElement = targetXmlDoc.CreateElement("Name");
studentNameXmlElement .InnerText = studentElement.Name;
studentXmlElement.AppendChild(studentIDXmlElement);
studentXmlElement.AppendChild(studentNameXmlElement);
studentsNode.AppendChild(childElement);
}
}
This projects both sets into an anonymous object List, makes comparisons, and gives you a set of anonymous objects that don't yet exist by which you can add to the out XML.
public static List<object> GetInStudents(XDocument sourceXmlDoc)
{
IEnumerable<XElement> inStudentsElements =
sourceXmlDoc.Root.Elements("Classes").Descendants("Class")
.Descendants("Students").Descendants("Student");
return inStudentsElements.Select(i =>
new { Id = i.Elements().First().Value,
Name = i.Elements().Last().Value }).Cast<object>().ToList();
}
public static List<object> GetOutStudents(XmlDocument targetXmlDoc)
{
XmlNodeList outStudentsElements = targetXmlDoc.GetElementsByTagName("Students")[0].ChildNodes;
var outStudentsList = new List<object>();
for (int i = 0; i < outStudentsElements.Count; i++)
{
outStudentsList.Add(new { Id = outStudentsElements[i].ChildNodes[0].InnerText,
Name = outStudentsElements[i].ChildNodes[1].InnerText });
}
return outStudentsList;
}
And you compare them as such:
var inStudents = GetInStudents(sourceXmlDoc);
var outStudents = GetOutStudents(targetXmlDoc);
if (inStudents.SequenceEqual(outStudents))
{
return;
}
else
{
var elementsToAdd = inStudents.Except(outStudents);
foreach (var element in elementsToAdd)
{
// create xmlNode with element properties, add element to xml
}
}

How Parse Xml Without serialization and store each node Xpath

I have following xml file. I want to parse it without Serilization in C#.
<StudentList>
<StudentCount>5</StudentCount>
<Student>
<Id>1</Id>
<Name>Abc</Name>
</Student>
<Student>
<Id>2</Id>
<Name>Efg</Name>
</Student>
<Student>
<Id>3</Id>
<Name>Ghi</Name>
</Student>
<Student>
<Id>4</Id>
<Name>Jkl</Name>
</Student>
<Student>
<Id>5</Id>
<Name>Mno</Name>
</Student>
</StudentList>
I want to store above xml data in List of Student class
List<Student>
Class Student
{
public int Id = 0;
public string Name = "";
}
Here i also want a Xpath of each value node
For Ex :
StudentList\StudentCount
StudentList\Student\Id
StudentList\Student\Name
Please help me
How do i achieve this???
Quick solution to get you started:
Create an XElement:
XElement studentXml = XElement.Parse (
#"<StudentList>
<StudentCount>5</StudentCount>
<Student>
<Id>1</Id>
<Name>Abc</Name>
</Student>
<Student>
<Id>2</Id>
<Name>Efg</Name>
</Student>
<Student>
<Id>3</Id>
<Name>Ghi</Name>
</Student>
<Student>
<Id>4</Id>
<Name>Jkl</Name>
</Student>
<Student>
<Id>5</Id>
<Name>Mno</Name>
</Student>
</StudentList>");
..And select from it:
var students =
studentXml
.Descendants()
.Where(node => node.Name == "Student")
.Select(node =>
new Student {
Id = int.Parse(node.Descendants()
.First(item => item.Name == "Id")
.Value),
Name = node.Descendants()
.First(item => item.Name == "Name")
.Value
});
Try to parse the .xml using LINQ (check LINQ to read XML for an example on how to do this). You should be able to implement your other requirements from there - try it yourself and ask for help if you get stuck while coding :)

Parsing (and keeping) XML structure into SQL Server

I'm looking to parse a relatively complex XML file through C# and store a selection of the data into a SQL Server '08 database. This is what I'm looking to extract from the XML file:
<educationSystem>
<school>
<name>Primary School</name>
<students>
<student id="123456789">
<name>Steve Jobs</name>
<other elements>More Data</other elements>
</student>
<student id="987654">
<name>Jony Ive</name>
<otherElements>More Data</otherElements>
</student>
</students>
</school>
<school>
<name>High School</name>
<students>
<student id="123456">
<name>Bill Gates</name>
<other elements>More Data</other elements>
</student>
<student id="987654">
<name>Steve Ballmer</name>
<otherElements>More Data</otherElements>
</student>
</students>
</school>
</educationSystem>
[Before you ask, no this isn't a school assignment - I'm using school/students as an example and because the original is a lot more sensitive.]
I'm able to (using XDocument/XElement) parse the XML file and get a list of all school names, student names and student ID's, but when this gets added to the database, I end up with the Bill Gates student entry being under a second school. It's all just line-by-line.
I'm looking to find a way to say, achieve this:
Foreach school
put it's name into an XElement
foreach student
grab the name and id put into XElements
Grab next school and repeat
I believe Linq would be the best way to achieve this, but I'm having trouble in how to get started with the process. Would anyone be able to point me in the right direction?
Edit: Here's the code I'm currently using to save data to the database. It processes a list at a time (hence things aren't related as they should be). I'll also be tidying up the SQL as well.
private void saveToDatabase (List<XElement> currentSet, String dataName)
{
SqlConnection connection = null;
try
{
string connectionString = ConfigurationManager.ConnectionStrings["connString"].ConnectionString + "; Asynchronous Processing=true";
connection = new SqlConnection(connectionString);
connection.Open();
foreach (XElement node in currentSet)
{
SqlCommand sqlCmd = new SqlCommand("INSERT INTO dbo.DatabaseName (" + dataName + ") VALUES ('" + node.Value + "')", connection);
sqlCmd.ExecuteNonQuery();
}
}
This LINQ will generate a Collection of Objects,with two properties
Name of the school
List of students(again a collection)
var result = XElement.Load("data.xml")
.Descendants("school")
.Select( x => new {
name = XElement.Parse(x.FirstNode.ToString()).Value,
students =x.Descendants("student")
.Select(stud => new {
id = stud.Attribute("id"),
name = XElement.Parse(stud.FirstNode.ToString()).Value})
.ToList()});
Note:The LINQ assumes <name> as the first node under <school> and <student> tags
Then you can use the foreach that you intended and it will work like a charm
foreach (var school in result)
{
var schoolName = school.name;
foreach (var student in school.students)
{
//Access student.id and student.name here
}
}
For this particular type of workings with XML data, you could use XML Serialization / Deserialization.
This will allow you to Deserialize your XML Data into a IEnumerable Class Object, Perform your LINQ Queries on this Class and then save to SQL.
Hope this helps.
Update: The original code example did not mention a namespace. Namespaces need to be either accounted for when searching for elements by XName or one needs to to search using the XName.LocalName property. Updated the example to show how to handle selecting elements in such a case.
namespace Stackover
{
using System;
using System.Xml.Linq;
class Program
{
private const string Xml = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<namespaceDocument xmlns=""http://www.namedspace/schemas"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:schemaLocation=""http://www.namedspace/schemas.xsd"">
<educationSystem>
<school>
<name>Primary School</name>
<students>
<student id=""123456789"">
<name>Steve Jobs</name>
<otherElements>
<dataA>data</dataA>
</otherElements>
</student>
<student id=""987654"">
<name>Jony Ive</name>
<otherElements>
<dataB>data</dataB>
</otherElements>
</student>
</students>
</school>
<school>
<name>High School</name>
<students>
<student id=""123456"">
<name>Bill Gates</name>
<otherElements>
<dataC>data</dataC>
</otherElements>
</student>
<student id=""987654"">
<name>Steve Ballmer</name>
<otherElements>
<dataD>data</dataD>
</otherElements>
</student>
</students>
</school>
</educationSystem>
</namespaceDocument>";
static void Main(string[] args)
{
var root = XElement.Parse(Xml);
XNamespace ns = "http://www.namedspace/schemas";
foreach(var school in root.Descendants(ns + "school")) // or root.Descendants().Where(e => e.Name.LocalName.Equals("school"));
{
Console.WriteLine(school.Element(ns + "name").Value);
foreach (var students in school.Elements(ns+ "students"))
{
foreach (var student in students.Elements())
{
Console.WriteLine(student.Attribute("id"));
Console.WriteLine(student.Name); // Name = namespace + XName
Console.WriteLine(student.Name.LocalName); // no namespace
}
}
}
}
}
}

Categories

Resources