How Parse Xml Without serialization and store each node Xpath - c#

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 :)

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" />

Remove children nodes in XML using Linq

I have an XML file with a list of parent nodes and childe nodes nested within the parent node, and I need to remove the child nodes when a specific criteria is been met.
Ex: Remove all contact nodes where the id = 1. How can I achieve this using linq and xml. This is my XML structure
<Events>
<Event>
<id>1</id>
<title>AA</title>
<start>2019-12-01T14:13:58.863</start>
<end>2019-12-01T15:13:58.787</end>
<contacts>
<contact>
<id>1</id>
<name>ABC</name>
</contact>
<contact>
<id>2</id>
<name>ABCD</name>
</contact>
<contact>
<id>3</id>
<name>ABCDE</name>
</contact>
</contacts>
</Event>
<Event>
<id>2</id>
<title>BB</title>
<start>2019-12-01T14:13:58.863</start>
<end>2019-12-01T15:13:58.787</end>
<contacts>
<contact>
<id>1</id>
<name>ABC</name>
</contact>
<contact>
<id>2</id>
<name>ABCD</name>
</contact>
<contact>
<id>3</id>
<name>ABCDE</name>
</contact>
</contacts>
</Event>
</Events>
You can get the XML nodes using this query
var query = xmlDoc.Descendants("contact").Where(e => e.Element("id").Value.Equals(id)).ToList();
And then run
query.Remove()
to remove the elements that were returned.
As Jon Skeet pointed out there is no need to do anything esoteric. Here is a complete example how to do it. Pure LINQ to XML.
c#, LINQ to XML
void Main()
{
const string inputXML = #"e:\Temp\MikeOconner.xml";
const string outputXML = #"e:\Temp\MikeOconner_output.xml";
XDocument xml = XDocument.Load(inputXML);
xml.Root.DescendantsAndSelf("contact")
.Where(r => (string)r.Element("id").Value == "1")
.Remove();
xml.Save(outputXML);
}

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

Xml attendance: output attendance of selected student 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.

linq to xml navigate through xml c#

I have some XML and need to be able to read the data within.
A sample of the XML is
<?xml version="1.0" ?>
<ConsumeLeadRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LeadType>Mortgage</LeadType>
<LeadXml>
<ns1:LeadAssigned xmlns:ns1="http://YaddaYadda" xmlns:ns0="http://YaddaYadda" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Lead>
<Reference>1234</Reference>
<Product>
<Mnemonic>Mortgage</Mnemonic>
<Description>Mortgage Leads</Description>
<SubType>Other</SubType>
</Product>
<ExtendedInfo>
<Mortgage>
<MortgageValue>75000</MortgageValue>
<MortgageValueLowerBound>1</MortgageValueLowerBound>
<MortgageValueUpperBound>500</MortgageValueUpperBound>
<PropertyValue>100000</PropertyValue>
<PropertyValueLowerBound>1</PropertyValueLowerBound>
<PropertyValueUpperBound>500</PropertyValueUpperBound>
<Adverse>false</Adverse>
<FirstTime>false</FirstTime>
</Mortgage>
</ExtendedInfo>
<Applicants>
<Applicant1>
<Title>Mr</Title>
<Forename>SampleForename</Forename>
<Surname>SampleSurname</Surname>
<DateOfBirth>1903-02-01</DateOfBirth>
<Smoker>false</Smoker>
<TelephoneNumbers>
<TelephoneNumber>
<Number>01244123456</Number>
<Type>Mobile</Type>
</TelephoneNumber>
</TelephoneNumbers>
<EmailAddresses>
<EmailAddress>
<Email>test#moneysupermarket.com</Email>
<Type>Business</Type>
</EmailAddress>
</EmailAddresses>
<Addresses>
<Address>
<Street>Sample Street</Street>
<District>Sample District</District>
<Town>Sample Town</Town>
<County>Sample County</County>
<Postcode>CH53UZ</Postcode>
<Type>Home</Type>
</Address>
</Addresses>
</Applicant1>
</Applicants>
</ns0:Lead>
<Assignment>
<Price>20</Price>
<AssignmentDateTime>2010-02-01T00:00:00</AssignmentDateTime>
<Subscription>
<Reference>1234</Reference>
<Subscriber>
<Title>Mr</Title>
<Forename>SampleForename</Forename>
<Surname>SampleSurname</Surname>
</Subscriber>
</Subscription>
<Account>
<Reference>1234</Reference>
<CompanyName>Sample Company</CompanyName>
</Account>
<LeadType>SampleLeadType</LeadType>
<TerritoryName>UNITED KINGDOM</TerritoryName>
</Assignment>
</ns1:LeadAssigned>
</LeadXml>
<AuthenticationUsername>Username</AuthenticationUsername>
<AuthenticationPassword>Password</AuthenticationPassword>
</ConsumeLeadRequest>
Using Linq to XML how do i navigate to the items?
Thanks
Sp
I have tried a few things like
XDocument Leads = XDocument.Load(#"C:\Users\Steven.Pentleton\AppData\Local\Temporary Projects\PAALeadImport\PAAExmple.xml");
var Lead = (from L in Leads.Descendants("Lead")
select new { LeadType = (string)L.Element("Reference") }).ToList();
var S = Lead.First();
string T = S.LeadType;
Are you looking for XDcoument or XElement in linq
Namespace: using System.Xml.Linq;
I guess you are looking for a Linq To Xml guide

Categories

Resources