I am new to unit testing and I am wondering what would be the best practices for unit testing xml deserialisation.
Consider the following xml:
<people>
<person id="1">
<name>Joe</name>
<age>28</age>
</person>
<person id="2">
<name>Jack</name>
<age>38</age>
</person>
</people>
And the following model class for the people:
[XmlRoot(ElementName ="people")]
public class People
{
public People() { PeopleList = new List<Person>(); }
[XmlElement("person")]
public List<Person> PeopleList { get; set; }
}
public class Person
{
[XmlAttribute("id")]
public int id { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("age")]
public int Age { get; set; }
}
I deserialize the xml using:
public List<Person> GetListOfPeople()
{
People plist = new People();
string content;
using (StreamReader sr = new StreamReader(manager.Open("People.xml")))
{
var serializer = new XmlSerializer(typeof(People));
plist = (People)serializer.Deserialize(sr);
}
return plist.PeopleList;
}
What would be the best methods to unit test the GetListOfPeople method above?
If you can change your method to take an xml file as an input parameter, you can have a sample xml file created and added to your test project. Since you know the values of your xml file, you can start comparing the values directly.
Considering you'll use the sample file you provided in your test, you can verify like this:
var persons = x.GetListOfPeople("sample.xml");
Assert.AreEqual("Joe", persons[0].Name);
Assert.AreEqual(38, persons[1].Age);
If the xml file is coming to your code from some source and you think it couldn't be following your xml schema all the time, then probably you can create some sample xml files again which violate your schema and prepare tests to call your method which should throw some exception if schema is not correct.
Hope this helps.
Related
I want to save a list of dates in an xml, I want to use dataset to achieve the task, I do the same to a database using Entity Framework. This allows me to access the dates using event.eventDates.start
but in the dataset I cannot achieve it.
public class Event
{
[Key]
public string id { get; set; }
public virtual ICollection<Date> eventDates { get; set; }
}
Date class
public class Date
{
public DateTime start { get; set; }
public DateTime end { get; set; }
}
When using entity framework I can access the eventDates Object using event.eventDates.start
I mapped the data from the sql database in the dataset builder the relations look like this
I want the xml file to be in this format
<?xml version="1.0" standalone="yes"?>
<db xmlns="http://tempuri.org/LocalDB.xsd">
<Event>
<id>ID</id>
<eventdates>
<date>
<startdate></startdate>
<enddate></enddate>
<date>
<date>
<startdate></startdate>
<enddate></enddate>
<date>
</eventdates>
</Event>
</db>
Is there any way to achieve that using datasets?
I'm new to C# any help would be appreciated
This code works, based on generate xml files based on my c# classes. I would rename your classes to something else than "Event" and "Date". These names are too generic and are usually protected by the system. ICollection is not serializable. Read XML serialization of interface property. You can use DTO or you can change the collection type (i.e. with List<>) and with XML serialization attributes avoid circular references and/or disable lazy load (i.e. use eagerly load using Include method) or the risk is that you serialize the whole database.
Program.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace EFToXml
{
public class MyEvent
{
public string Id { get; set; }
public virtual List<MyDate> EventDates { get; set; }
}
public class MyDate
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
var myDate1 = new MyDate
{
Start = DateTime.Now,
End = DateTime.Now.AddDays(1)
};
var eventDates = new List<MyDate> { myDate1 };
var myEvent = new MyEvent
{
Id = "1",
EventDates = eventDates
};
XmlSerializer serializer = new XmlSerializer(typeof(MyEvent));
serializer.Serialize(File.Create(#"C:\Users\<UserName>\Source\Repos\myEvents.xml"), myEvent);
}
}
}
myEvents.xml:
<?xml version="1.0"?>
<MyEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<EventDates>
<MyDate>
<Start>2019-11-24T21:52:04.5032671+01:00</Start>
<End>2019-11-25T21:52:04.5194026+01:00</End>
</MyDate>
</EventDates>
</MyEvent>
If i understand correctly, you just want to reproduce EntityFramework's behavior using DataSets.
You can achieve exactly same thing simply creating DataSet (using integrated VisualStudio designer) with proper tables, fields and relations:
later on you can access your data using code like this:
var ds = new DataSet1();
var ue = ds.UserEvents.FirstOrDefault();
var ued = ue.GetChildRows("FK_UserEvents_EventDates")
.Cast<DataSet1.EventDatesRow>();
var date = ued.FirstOrDefault().Date;
Next thing to do is serialization - it's quite easy:
Serialization example
In my c# code I am trying to use XML serialization instead of xml linq. I am not sure which one is better.
But for now the issue I am having is that my node is coming twice. So this is my class structure
public class Enterprise
{
public Properties properties;
public List<Person> person;
}
This is the xml I get generated
<?xml version="1.0" encoding="iso-8859-1"?>
<enterprise>
<properties lang="nb">
<Comments>Utlasting av LMS-data</Comments>
<Datasource>SIKT</Datasource>
<Target />
<Datetime>onsdag 24. oktober 2018</Datetime>
</properties>
<person>
<Person>
<sourceid>
<Source>AD</Source>
<Id>123</Id>
</sourceid>
<Userid>mohsin</Userid>
</Person>
As you see the Person tag is coming twice. This is how it is set up
Enterprise enterprise = new Enterprise();
enterprise.properties.LanguageCode = "nb";
enterprise.properties.Comments = "Utlasting av LMS-data";
enterprise.properties.Datasource = "SIKT";
enterprise.properties.Target = "";
enterprise.properties.Datetime = DateTime.Now.ToLongDateString();
List<Person> person = new List<Person>();
person.Add(new Person
{
//sourceid = new SourceId
//{
// Id = "123",
// Source = "AD"
//},
Userid = "mohsin"
});
enterprise.person = person;
Does anyone knows the reason?
When you use List or array it will conside as `XmlArrayItem' to overcome use 'XmlElement'
public class Enterprise
{
public Properties properties;
[XmlElement("person")]
public List<Person> person;
}
I would like to know how can I read a XML file from my desktop and put it into a string?
Here is my XML:
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
I want to store each user, but still detect if their small or big, is there a way to do this?
Before you downrate this, you might want to check google because I did research, but found nothing.
"Before you downrate this, you might want to check google because I
did research, but found nothing"
You found nothing because you don't know what you are searching for, also your XML is invalid, you need to enclose it in a rootElement. Then the first thing you need to do is read this file from the desktop if it exists.
You can check the size if you wish at that time and determine if this is "too large" even though it doesn't really matter. I highly doubt your XML file will be 5+ GB in size. If it is then you need an alternative, no single object in a .Net program may be over 2GB, the best you could do is 1,073,741,823 on a 64bit machine.
For very large XML files, anything above 1.0 GB, combine XmlReader and LINQ as stated by Jon Skeet here:
If your document is particularly huge, you can combine XmlReader and
LINQ to XML by creating an XElement from an XmlReader for each of your
"outer" elements in a streaming manner: this lets you do most of the
conversion work in LINQ to XML, but still only need a small portion of
the document in memory at any one time.
For small XML files, anything 1.0 GB or lower stick to the DOM as shown below.
With that said, what you need is to learn what Serialization and Deserialization mean.
Serialize convert an object instance to an XML document.
Deserialize convert an XML document into an object instance.
Instead of XML you can also use JSON, binary, etc.
In your case this is what can be done to Deserialize this XML document back into an Object in order for you to use in your code.
First fix up the XML and give it a Root.
<?xml version="1.0" encoding="UTF-8"?>
<DataRoot>
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
</DataRoot>
Then create the root class in C#, you may generate this directly in Visual Studio 2012+ by copying your XML and going to Edit - Paste Special, but I like to use: XML to C# Class Generator
Here is what your code would look like after you generate the C# Root Class for your XML, hope it helps you understand it better.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class Program
{
[XmlRoot(ElementName = "user")]
public class User
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "motto")]
public string Motto { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "smallusers")]
public class Smallusers
{
[XmlElement(ElementName = "user")]
public List<User> User { get; set; }
}
[XmlRoot(ElementName = "bigusers")]
public class Bigusers
{
[XmlElement(ElementName = "user")]
public User User { get; set; }
}
[XmlRoot(ElementName = "DataRoot")]
public class DataRoot
{
[XmlElement(ElementName = "smallusers")]
public Smallusers Smallusers { get; set; }
[XmlElement(ElementName = "bigusers")]
public Bigusers Bigusers { get; set; }
}
static void Main(string[] args)
{
string testXMLData = #"<DataRoot><smallusers><user id=""1""><name>John</name><motto>I am john, who are you?</motto></user><user id=""2""><name>Peter</name><motto>Hello everyone!</motto></user></smallusers><bigusers><user id=""3""><name>Barry</name><motto>Earth is awesome</motto></user></bigusers></DataRoot>";
var fileXmlData = File.ReadAllText(#"C:\XMLFile.xml");
var deserializedObject = DeserializeFromXML(fileXmlData);
var serializedToXML = SerializeToXml(deserializedObject);
//I want to store each user, but still detect if their small or big, is there a way to do this?
foreach (var smallUser in deserializedObject.Smallusers.User)
{
//Iterating your collection of Small users?
//Do what you need here with `smalluser`.
var name = smallUser.Name; //Example...
}
Console.WriteLine(serializedToXML);
Console.ReadKey();
}
public static string SerializeToXml(DataRoot DataObject)
{
var xsSubmit = new XmlSerializer(typeof(DataRoot));
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
xsSubmit.Serialize(writer, DataObject);
var data = sw.ToString();
writer.Flush();
writer.Close();
sw.Flush();
sw.Close();
return data;
}
}
}
public static DataRoot DeserializeFromXML(string xml)
{
var xsExpirations = new XmlSerializer(typeof(DataRoot));
DataRoot rootDataObj = null;
using (TextReader reader = new StringReader(xml))
{
rootDataObj = (DataRoot)xsExpirations.Deserialize(reader);
reader.Close();
}
return rootDataObj;
}
}
}
I need to take in a text document from a server and create an XML hierarchy from it in order to use it in a C# program.
It will be an organizational hierarchy. Here is the same of the text file:
EmployeeID; Employee Name; SupervisorID
1; Bob; 3
2; Mark; 1
3; Jill; 0
4; Ann ; 1
Where the above relationships would be:
Bob's boss is Jill. Mark's boss is Bob, and Jill has no boss.
I want to create an XML file from that data to look something like this:
<Employee> Jill
<Employee> Bob
<Employee> Mark </Employee>
<Employee> Ann </Employee>
</Employee>
</Employee>
I don't know if this makes sense, because I have never worked with C# or XML before.
Feel free to change how the tags are named, the main things I will need to do:
Be able to get the names of everyone supervised by the same person. (Ex: Mark would want Mark and Ann)
Be able to get the names of all supervisors above an employee (Ex: Mark's would be Bob and Jill)
Be able to get the names of all people under then (Ex: Mark would have nobody, Jill would have everybody, Bob would have Mark and Ann)
I've looked at XElement and XDoc and various tutorials and SO questions but most of the SO questions are too advanced for me at this point, and most of the tutorials aren't trying to create a hierarchy like mine.
Define a class Employee:
public class Employee{
[XmlIgnore]
public int ID{get;set;}
[XmlIgnore]
public int BossID{get;set;}
[XmlText]
public string Name{get;set;}
[XmlElement("Employee")
public Employee[] Minions{get;set;}
public SetMinions(List<Employee> list){
Minions = list.Where(e=>e.BossID==ID).ToArray();
}
}
then parse your input:
List<Employee> list = File.ReadAllLines("MyInputFile.txt")
.Select(l=>new Emplyee(){
ID = l.Split(';')[0],
Name= l.Split(';')[1],
BossID = l.Split(';')[2]}).ToList();
and then set the minions for each:
list.ForEach(e=>e.SetMinions(list));
And produce the XML like this:
XmlSerializer xser = new XmlSerializer(typeof(Employee));
xser.Serialize(File.OpenWrite("output.txt", list.First(e=>e.BossID==0)));
If it's not obvious this code is quite dirty and unreliable, add some checks and cleanups
Employee class:
public class Employee
{
public Employee()
{
Subordinates = new List<Employee>();
}
public int Id { get; set; }
public string Name { get; set; }
public int SupervisorId { get; set; }
public List<Employee> Subordinates { get; private set; }
public XElement ToXml()
{
return new XElement("Employee",
new XElement("Id", Id),
new XElement("Name", Name),
new XElement("Subordinates", Subordinates.Select(s => s.ToXml())));
}
}
And parsing logic:
// dictionary as a storage for data read from file
var Employees = new Dictionary<int,Employee>();
// file reading
var file = File.Open("Input.txt", FileMode.Open);
using (var reader = new StreamReader(file))
{
reader.ReadLine();
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split(';');
var newEmployee = new Employee { Id = int.Parse(fields[0]), Name = fields[1].Trim(), SupervisorId = int.Parse(fields[2]) };
Employees.Add(newEmployee.Id, newEmployee);
}
}
// filling Subordinates within every employee
foreach (var emp in Employees.Values)
{
if (Employees.ContainsKey(emp.SupervisorId))
Employees[emp.SupervisorId].Subordinates.Add(emp);
}
// taking first (root) employee by selecting the one with supervisorId == 0
var first = Employees.Values.First(e => e.SupervisorId == 0);
// XML generation
var xml = first.ToXml();
Result that I received from that code with your sample input:
<Employee>
<Id>3</Id>
<Name>Jill</Name>
<Subordinates>
<Employee>
<Id>1</Id>
<Name>Bob</Name>
<Subordinates>
<Employee>
<Id>2</Id>
<Name>Mark</Name>
<Subordinates />
</Employee>
<Employee>
<Id>4</Id>
<Name>Ann</Name>
<Subordinates />
</Employee>
</Subordinates>
</Employee>
</Subordinates>
</Employee>
I think that structure is better than you suggested. However, you can easily modify XML structure by modifying ToXml method within Employee class. Following one would give output you suggested:
public XElement ToXml()
{
return new XElement("Employee",
new XText(Name),
Subordinates.Select(s => s.ToXml()));
}
Result:
<Employee>Jill
<Employee>Bob
<Employee>Mark</Employee>
<Employee>Ann</Employee>
</Employee>
</Employee>
I need to ask a general question. I don't have the code in front of me because I'm writing this on my iPhone.
I have a Class that represents a certain XML schema. I have a SPROC that returns this XML. What I need to do is deserialize the XML to this Class.
XML:
<xml>
<person>
<firstName>Bob</firstName>
<lastName>Robby</lastName>
</person>
</xml>
I need to deserialize this XML into the custom Person Class so I can loop through this Model and spit it out in the View. I'm sure there's some kind of casting involved, I just don't know how to do it.
My Solution:
public class Program {
public static void Main(string[] args) {
string xml = #"<xml><person><firstName>Bob</firstName><lastName>Robby</lastName></person></xml>";
var doc = XElement.Parse(xml);
var person = (from x in doc.Elements("person") select x).FirstOrDefault();
XmlSerializer serializer = new XmlSerializer(typeof(Person));
var sr = new StringReader(person.ToString());
// Use the Deserialize method to restore the object's state.
var myPerson = (Person)serializer.Deserialize(sr);
}
}
And Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication3 {
[XmlRoot("person")]
public class Person {
[XmlElement("firstName")]
public string FirstName { get; set; }
[XmlElement("lastName")]
public string LastName { get; set; }
}
}
in linq it would be something like this
XDocument xmlFile = XDocument.Parse(yourXml)
var people = (from x in xmlFile.Descendants("person")
select new Person(){
firstname = (string)x.Element("firstname").Value,
lastname = (string)x.Element("lastname").Value
});