How to add the data from different class in one XML file? - c#

I have a three classes i want to add the all classes data in one XML file?
fist class is:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public override string ToString()
{
return FirstName + " " +LastName + " " +Email;
}
}
second class is :
public class Student:Person
{
public Student()
{
}
public double AssessmentGrade { get; set; }
public double AssignmentGrade { get; set; }
public override string ToString()
{
return base.ToString() + "," +AssessmentGrade + "," + AssignmentGrade;
}
}
Third Class is:
public class Teacher:Person
{
public string RoomNumber
{
get;
set;
}
public override string ToString()
{
return base.ToString() + "," + RoomNumber;
}
}
I made a DB Class too where i store all the data in XML file.
private const string path = #"Persons.xml";
Public static void SavePersons(List<Person> Persons)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = (" ");
XmlWriter xmlOut = XmlWriter.Create(path, settings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Persons");
foreach (Person person in Persons)
{
Student student = new Student();
Teacher teacher = new Teacher();
xmlOut.WriteStartElement("Person");
xmlOut.WriteAttributeString("Email", person.Email);
xmlOut.WriteElementString("Firstname", person.FirstName);
xmlOut.WriteElementString("Lastname", person.LastName);
xmlOut.WriteElementString("AssessmentGrade",
student.AssessmentGrade.ToString());
xmlOut.WriteElementString("AssignmentGrade",
student.AssignmentGrade.ToString());
xmlOut.WriteElementString("Room", teacher.RoomNumber.ToString());
xmlOut.WriteEndElement();
}
xmlOut.WriteEndElement();
xmlOut.Close();
}
Now problem is this how to add Student Class and Teacher class in the Same XML.I tried to add but it doesn't work.Can you please help me.

I think what you want to do is store Person, Student and Teacher separately, but in the same file, and using the same logic? Maybe something like this:
foreach (Person person in Persons)
{
// Edit: After reading the comment below, I
// updated the methods to return an XmlWriter. This
// way, you should be able to contiune working with the
// same instance:
if(typeOf(Student).IsInstanceOfType(person)){
xmlOut = AppendStudentInfo(person, xmlOut);
}else if(typeOf(Teacher).IsInstanceOfType(person){
xmlOut = AppendTeacherInfo(person, xmlOut);
}else{
xmlOut = AppendPersonInfo(person, xmlOut);
}
}
xmlOut.WriteEndElement();
xmlOut.Close();
(...)
Then create three helper-methods to write the data. This is an example for Students:
// (Edit: Updated to return an XmlWriter)
private XmlWriter AppendStudentInfo(Person person, XmlWriter xmlOut){
Student student = (Student) person;
// For teacher, use this instead:
// Teacher teacher = (Teacher) person;
xmlOut.WriteStartElement("Student");
xmlOut.WriteAttributeString("Email", student.Email);
xmlOut.WriteElementString("Firstname", student.FirstName);
xmlOut.WriteElementString("Lastname", student.LastName);
xmlOut.WriteElementString("AssessmentGrade",
student.AssessmentGrade.ToString());
xmlOut.WriteElementString("AssignmentGrade",
student.AssignmentGrade.ToString());
xmlOut.WriteEndElement();
// (Edit: Updated to return an XmlWriter)
return xmlOut;
}
Now for Teacher, you would do the same, but using that type instead of Student, and including fields relevant to the teacher-type (ie Room instead of AssignmentGrade and AssessmentGrade).
For Person, you would do the same, but only include the fields that exist in the class Person.
I haven't tested this code, but believe it should be more or less correct. Hope this was something like the answer you were looking for.

From what I can see in your code it should be erroring out on these three lines. :
xmlOut.WriteElementString("AssessmentGrade", student.AssessmentGrade.ToString());
xmlOut.WriteElementString("AssignmentGrade", student.AssignmentGrade.ToString());
xmlOut.WriteElementString("Room", teacher.RoomNumber.ToString());
This is because you declare your new student and teacher objects above but you do not provide a value for AssessmentGrade/AssignmentGrade/RoomNumber.
Am I understanding what your issue is?
student.AssessmentGrade = 90;

Use DataContractSerializer to serialize data in xml format:
Public static void SavePersons(Persons persons)
{
var serializer = new DataContractSerializer(typeof(Persons));
// Create a FileStream to write with.
FileStream fs = new FileStream("file.xml", FileMode.Create);
using (fs)
{
ser.WriteObject(fs, p);
}
}
Class Persons:
[KnownType(typeof(Student))]
[KnownType(typeof(Teacher))]
[DataContract]
public class Persons
{
[DataMember]
public IEnumerable<Person>{get;set;}
}
Another example on it:MSDN article

Related

How to call a method multiple times at once for a file

I have a Person model class as following;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
I get the FirstName and LastName from the user and validate them. Then I create a user name for each first and last name entered by the user with the following method;
public static string CreateUserName(Person person)
{
return $"{person.LastName}{person.FirstName.Substring(0,2)}";
}
I'm able to do this one person at a time by running the program manually each time. What I want to know how to do is, suppose I have multiple lines of data stored in a CSV file which consists of FirstName and LastName and I read that file, then generate a user name for each row in that file at once with my CreateUserName method.
The output will be printed on the console and Person class will be used for other operations also.
Csv content:
FirstName;LastName
FirstName1;LastName1
FirstName2;LastName2
FirstName3;LastName3
Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Logins
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Program
{
public static string CreateUserName(Person person) =>
$"{person.LastName}{person.FirstName.Substring(0, 2)}";
static void Main(string[] args)
{
var lines = File.ReadAllLines("test.csv");
var result = lines.Skip(1)
.Aggregate(new Dictionary<string, Person>(), (dict, x) =>
{
var items = x.Split(';');
var person = new Person { FirstName = items[0], LastName = items[1] };
if (!dict.TryAdd(CreateUserName(person), person))
throw new Exception("User with such username already exists!");
return dict;
});
foreach (var item in result)
{
Console.WriteLine($"Username: '{item.Key}' for {item.Value.FirstName} {item.Value.LastName}");
}
}
}
}
Result:
Username: 'LastName1Fi' for FirstName1 LastName1
Username: 'LastName2Fi' for FirstName2 LastName2
Username: 'LastName3Fi' for FirstName3 LastName3
Consider the following demo code:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName
// Calculate UserName from first and last
=> $"{LastName}{FirstName.Substring(0, 2)}";
public static Person Parse(string csvLine)
// Transform a text line "First, Last" into
// an object of Person
{
var parts = csvLine.Split(',');
if (parts.Length>=2)
{
return new Person()
{
FirstName = parts[0].Trim(),
LastName = parts[1].Trim()
};
}
throw new ArgumentException(nameof(csvLine));
}
}
class Program
{
static void Main(string[] args)
{
var csv = #"First,Last
Jack, Mas
Jake, Moon
John, Mira
Jonas, Master
Jacob, Meek
Josselyn, Moore
Joanna, Milka";
var fs = new System.IO.StringReader(csv);
fs.ReadLine(); // eat up first line
var list = new List<Person>();
while (fs.Peek()!=-1)
{
var person = Person.Parse(fs.ReadLine());
list.Add(person);
}
// create a list of usernames, each in a new line
var unames = string.Join(
Environment.NewLine,
list.Select((person) => person.UserName));
Console.WriteLine(unames);
}
}
That outputs

storing Instances of student into an array

Im trying to store these instances of student inside of this array.
Student:
public Student(string Value)
{
FirstName = Value;
LastName = Value;
StudentID = Value;
}
Here is the Array
string[] student = new string[4];
{ ElementarySchoolStudent, MiddleSchoolStudent, HighSchoolStudent, CollegeStudent }
Each constructor is like this and they use the Student method.
CollegeStudent(string value) : base(value) { }
How exactly would I go about storing these instances in the array? When I do it like that i get the error message:
college student is a type which is not valid in the given context.
What's the correct way to code it exactly?
From this and the last question, maybe you want something like this
Given
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string StudentID { get; set; }
}
Usage
var students = new Student[]
{
new Student(){FirstName = "bob",LastName = "blerg",StudentID = "23432"},
new Student(){FirstName = "dan",LastName = "flib",StudentID = "4564"},
new Student(){FirstName = "jib",LastName = "jab",StudentID = "564"},
};
foreach (var student in students)
Console.WriteLine($"{student.StudentID} : {student.FirstName}, {student.LastName}");
Output
23432 : bob, blerg
4564 : dan, flib
564 : jib, jab
Note : Add constructor or pepper and salt to taste

Object Initializer with expression

Is it possible to inilialize an object like this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApp3
{
class Student
{
public bool IsStudent { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
/*
************Concise but not complied***********
var student = GetDefault()
{
Age = 18,
Name = "Nick"
};
*/
var student = GetDefault();
student.Age = 18;
student.Name = "Nick";
Student GetDefault()
{
var stu = new Student()
{
IsStudent = true
};
return stu;
}
}
}
}
I think the repeated "student." is redundant.
What I want to talking about is a possiable C# syntactic sugar,not object initialize solution.The properties of Student may be a lot.
If it's not possible,it's apreciate to tell the possible design reason.
I agree with HimBromBeere's statements. If you really want syntax close to what you described, you could do something like the following. That being said, obviously this now changes the meaning of the method and arguably this isn't really a great 'style' in my opinion... but it does achieve a syntax close to what you're asking for:
public class Student
{
public bool IsStudent { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
public static void Main(string[] args)
{
Student GetDefault(params Action<Student>[] modifiers)
{
var stu = new Student
{
IsStudent = true
};
if (modifiers != null)
{
foreach (var modifier in modifiers)
{
modifier(stu);
}
}
return stu;
}
var student = GetDefault(
s => s.Age = 18,
s => s.Name = "Nick"
);
}
You Should try something like this:
using System;
namespace Problem
{
public class Student
{
public bool IsStudent { get; set; }
public int? Age { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
// GetDefault.
Student Student_One = GetDefault();
Console.WriteLine("IsStudent = {0} \nName = {1} \nAge = {2}", Student_One.IsStudent, Student_One.Age, Student_One.Name);
Console.WriteLine("-------------------------");
Student Student_Two = GetDefault();
Student_Two.Age = 19;
Student_Two.Name = "Nick";
Console.WriteLine("IsStudent = {0} \nName = {1} \nAge = {2}", Student_Two.IsStudent, Student_Two.Age, Student_Two.Name);
Console.WriteLine("-------------------------");
Student Student_Three = new Student
{
IsStudent = true,
Age = 20,
Name = "Johnson"
};
Console.WriteLine("Name = {0} & Age = {1}" , Student_Three.Age, Student_Three.Name);
Console.WriteLine();
}
static Student GetDefault()
{
Student student = new Student
{
IsStudent = true
};
return student;
}
}
}
Output:
Your assumption - though comprehensible - is wrong. student. is not redundant.
Imagine someone would just call GetDefault without asigning its result to a variable - which is pure fine and valid.
GetDefault()
{
...
}
GetDefault returns an instance whose properties you modify afterwards. However as you don´t assign this object to any variable it will be garbage-collected soon. So when you set the properties it won´t have any efect at all, because there´s no code that may read it. In particular you can´t call DoSomethingWithUpdatedValues on that instance, because the instance is lost.
The syntax you want to avoid is the only valid syntax to achieve this. You could of course add parameters to your method, which however would change the methods purpose.
The cleanest solution in my opinion is to have a constructor that accepts the two parameters. There´s actually not much you can do on a Student that has no name or age at all, so just provide it to the constructor.

how to acess list in a class without an instance of the class?

I am making a Tennis program in c# for an exam and I need to read a file with players. I can read the players into a list using this code. Now i need to acess the list MalePlayers from another class to get two players from the list and run a match but I can't figure out how to acess it.
public class MaleReader
{
private List<Player> MalePlayers = new List<Player>();
public string FileName { get; set; }
public string Delimiter { get; set; }
public MaleReader(string fn, string delim = "|")
{
FileName = fn;
Delimiter = delim;
}
public override string ToString()
{
var rv = "";
foreach (var p in MalePlayers)
rv += "Player: " + p + "\n";
return rv;
}
public void Load()
{
TextFieldParser par = new TextFieldParser(FileName);
par.TextFieldType = FieldType.Delimited;
par.SetDelimiters(Delimiter);
while (!par.EndOfData)
{
string[] fields = par.ReadFields();
int MID = Int32.Parse(fields[0]);
string Firstname = fields[1];
string MiddleName = fields[2];
string LastName = fields[3];
DateTime DoB = DateTime.Parse(fields[4]);
string Country = fields[5];
string ShortCountry = fields[6];
//Gender gender =
var p = new Player(MID, Enums.Gender.Male, Firstname, MiddleName, LastName, DoB, Country, ShortCountry);
MalePlayers.Add(p);
}
par.Close();
}
public static void RunPlayerReaderMale()
{
var f = new MaleReader(#"C:\Users\R0ede\Dropbox\Git\Examprogram\Data\FemalePlayer.txt");
f.Load();
//Console.WriteLine(f);
}
}
Is it possible to do without making an instance of the class?
You have several ways to do that, and most of them are just a style choice.
One option would be create a static method which internally creates the MaleReader instance but it only returns its MalePlayers property.
public static List<Player> RunPlayerReaderMale()
{
var f = new MaleReader(#"C:\Users\R0ede\Dropbox\Git\Examprogram\Data\MalePlayer.txt");
f.Load();
return f.MalePlayers;
}
I wonder why don't you have an instance of the class MaleReader? I assume you had to create it to load players from the file.
I would either keep the instance you used to read the file, or, if you don't want to keep it, extract the List before you get rid of the instance.
Or you can take the static aproach, as others mentioned.
You can only do that by making it: public static List<Player> MalePlayers = new List<Player>();
But you need to understand what you're doing here, making this list static means that all instances of your class will share the same List.

how to delete multiple objects in a list of objects through its properties

I'm back with another question regarding objects again.
Do note I'm just one month into programming in general so I'm sorry if this is a dumb question.
Right now I'm doing some homework that has me design a library class and a books class. This library is supposed to store a number of books. I'm supposed to manipulate this list of objects with some methods. Most of the methods I know how to design except one. I am supposed to search this list for all books with a certain author and delete them. I don't know how to approach this. I am able to delete singular objects from the list through the properties of the book class but I dont know how to delete multiple objects of it.
class Library
{
public string Name { get; set; }
public List<Book> list = new List<Book>();
public List<Book> foundBooks = new List<Book>();
public Library(string name)
{
this.Name = name;
}
//Library Methods
public void AddBook(Book book)
{
list.Add(book);
}
public void PrintBookInfo(Book book)
{
Console.WriteLine("Title: "+ book.Title);
Console.WriteLine("Author " +book.Author);
Console.WriteLine("Publisher: "+ book.Publisher);
Console.WriteLine("Release Date: " + book.ReleaseDate.ToString());
Console.WriteLine("ISBN: " + book.ISBN);
Console.WriteLine();
}
public void PrintAllBooks()
{
foreach (Book book in list)
{
PrintBookInfo(book);
}
}
public Book SearchForBook(string name)
{
foreach (Book book in list)
{
if (book.Author == name)
{
PrintBookInfo(book);
return book;
}
}
return null;
}
public void DeleteBook(Book book)// this is the method im using to delete objects but I don't know how to extend this.
{
list.Remove(book);
}
}
Here is my book class
class Book
{
//Properties
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
public DateTime ReleaseDate { get; set; }
public int ISBN { get; set; }
public Book(string Title,
string Author,
string Publisher,
DateTime ReleaseDate,
int ISBN)
{
this.Title = Title;
this.Author = Author;
this.Publisher = Publisher;
this.ReleaseDate = ReleaseDate;
this.ISBN = ISBN;
}
}
Finally my main so far:
class Program
{
static void Main(string[] args)
{
var library = new Library("Yishun Regional Library");
var bookOne = new Book("Harry Potter", "JK Rowling", "UK", DateTime.Today, 1165);
var bookTwo = new Book("Eragon", "Paolini", "Netherlands", DateTime.Today, 2565);
var bookThree = new Book("Harry", "JK Rowling", "UK", DateTime.Today, 1145);
library.AddBook(bookOne);
library.AddBook(bookTwo);
library.AddBook(bookThree);
Console.WriteLine("Hello Welcome To Yishun Library. Take a look at the books Availabe.");
Console.WriteLine();
foreach (Book book in library.list)
{
library.PrintBookInfo(book);
}
Console.WriteLine();
string name;
Console.WriteLine("Enter name of author: ");
name = Console.ReadLine();
var bookFound = library.SearchForBook(name);
library.DeleteBook(bookFound);
Console.WriteLine("Current list of books: \n");
library.PrintAllBooks();
}
One way I thought to implement this is to create an array of books found and then delete the books using foreach method but I don't know how to implement this. Or rather I quickly found out that you cant manipulate the data using foreach. Any help would be appreciated thanks! ;]
The trick is to use for loop instead of foreach one and loop backward:
for (int i = list.Count - 1; i >= 0; --i)
if (String.Equals(list[i].Author, "AuthorToDelete"))
list.RemoveAt(i);
In case of looping forward the scheme is more complex:
for (int i = 0; i < list.Count;)
if (String.Equals(list[i].Author, "AuthorToDelete"))
list.RemoveAt(i);
else
i += 1;
Or just call (is it accepted in homework?)
list.RemoveAll(book => String.Equals(book.Author, "AuthorToDelete"));
In your Library class:
public void DeleteBooksByAuthor(string authorName)
{
list.RemoveAll(x => x.Author == authorName);
}
Hi use the below code in your class. It would handle null check on your list object as well
public void DeleteBooksByAuthor(string authorName)
{
list?.RemoveAll(x => x?.Author == authorName);
}

Categories