Reputation: 97
I currently playing with the XMLSerializer
to understand how it works. I am able to serialize, save and de-serialize a single object without problem. However I run into problems when I try to de-serialize multiple objects. I get this error : Unhandled exception. System.InvalidOperationException: There is an error in XML document (10, 10).
---> System.Xml.XmlException: Unexpected XML declaration. The XML declaration must be the first node in the document, and no whitespace characters are allowed to appear before it.
I've tried this approach https://stackoverflow.com/a/16416636/8964654 here (and I could be doing it wrong)
public static ICollection<T> DeserializeList<T>()
{
string filePath = @"TextFiles/Users.txt";
XmlSerializer serializerTool = new XmlSerializer(typeof(User));
List<T> list = new List<T>();
using (FileStream fs = new FileStream (filePath, FileMode.Open)){
while(fs.Position!=fs.Length)
{
//deserialize each object in the file
var deserialized = (T)serializerTool.Deserialize(fs);
//add individual object to a list
list.Add(deserialized);
}
}
//return the list of objects
return list;
}
it didn't work
This is my original code. I intentionally call the SaveUser
method twice to simulate the method being called twice at different times
[Serializable]
public class User: ISerializable{
public static void SaveUser(User user){
string filePath = @"TextFiles/Users.txt";
XmlSerializer serializerTool = new XmlSerializer(typeof(User));
using(FileStream fs = new FileStream(filePath, FileMode.Append)){
serializerTool.Serialize(fs, user);
}
}
public static void PrintUser(){
string filePath = @"TextFiles/Users.txt";
XmlSerializer serializerTool = new XmlSerializer(typeof(User));
using (FileStream fs = new FileStream (filePath, FileMode.Open)){
User u1 = (User)serializerTool.Deserialize(fs);
Console.WriteLine($"{u1.FirstName} {u1.LastName}, {u1.DOB.ToShortDateString()}");
}
}
}
class Program
{
static void Main(string[] args)
{
User user1 = new User(){
FirstName = "Kim",
LastName = "Styles",
Address = "500 Penn street, Dallas, 46589",
Username = "[email protected]",
Password ="Kim2019",
DOB = (new DateTime(1990,10,01)),
Id = 2
};
User user2 = new User(){
FirstName = "Carlos",
LastName = "Santana",
Address = "500 Amigos street,San Jose, California, 46589",
Username = "[email protected]",
Password ="CarLosSan2019",
DOB = (new DateTime(1990,10,01)),
Id = 2
};
User.SaveUser(user1);
User.SaveUser(user2);
User.PrintUser();
}
}
below is how it saved XML data
<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>Kim</FirstName>
<LastName>Styles</LastName>
<DOBProxy>Monday, 01 October 1990</DOBProxy>
<Username>[email protected]</Username>
<Password>Kim2019</Password>
<Address>500 Penn street, Dallas, 46589</Address>
<Id>1</Id>
</User>
<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName>Carlos</FirstName>
<LastName>Santana</LastName>
<DOBProxy>Monday, 01 October 1990</DOBProxy>
<Username>[email protected]</Username>
<Password>CarLosSan2019</Password>
<Address>500 Amigos street,San Jose, California, 46589</Address>
<Id>2</Id>
</User>
I want to be able to retrieve all the data and print details of each individual user. How can I do this? Is there a better approach?
Upvotes: 0
Views: 448
Reputation:
I'd solve this problem as follow:
Create the User class
A Serializable class contains a user details.
[Serializable]
public class User
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DOB { get; set; }
public override string ToString()
{
return $"{ID}, {FirstName}, {LastName}, {DOB.ToShortDateString()}";
}
}
Create the Users class
Another Serializable class contains a list of User
objects and handles both serialize and Deserialize routines:
[Serializable]
public class Users
{
public List<User> ThisUsers = new List<User>();
public void Save(string filePath)
{
XmlSerializer xs = new XmlSerializer(typeof(Users));
using (StreamWriter sr = new StreamWriter(filePath))
{
xs.Serialize(sr, this);
}
}
public static Users Load(string filePath)
{
Users users;
XmlSerializer xs = new XmlSerializer(typeof(Users));
using (StreamReader sr = new StreamReader(filePath))
{
users = (Users)xs.Deserialize(sr);
}
return users;
}
}
This way, you guarantee the XML file is formatted correctly, manage the users list (add, remove, edit).
Save (serialize) example
string filePath = @"TextFiles/Users.txt";
Users users = new Users();
for (int i = 1; i < 5; i++)
{
User u = new User
{
ID = i,
FirstName = $"User {i}",
LastName = $"Last Name {i}",
DOB = DateTime.Now.AddYears(-30 + i)
};
users.ThisUsers.Add(u);
}
users.Save(filePath);
Load (Deserialize) example:
string filePath = @"TextFiles/Users.txt";
Users users = Users.Load(filePath);
users.ThisUsers.ForEach(a => Console.WriteLine(a.ToString()));
//Or get a specific user by id:
Console.WriteLine(users.ThisUsers.Where(b => b.ID == 3).FirstOrDefault()?.ToString());
and here is how the generated XML file looks like
<?xml version="1.0" encoding="utf-8"?>
<Users xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ThisUsers>
<User>
<ID>1</ID>
<FirstName>User 1</FirstName>
<LastName>Last Name 1</LastName>
<DOB>1990-11-04T08:16:09.1099698+03:00</DOB>
</User>
<User>
<ID>2</ID>
<FirstName>User 2</FirstName>
<LastName>Last Name 2</LastName>
<DOB>1991-11-04T08:16:09.1109688+03:00</DOB>
</User>
<User>
<ID>3</ID>
<FirstName>User 3</FirstName>
<LastName>Last Name 3</LastName>
<DOB>1992-11-04T08:16:09.1109688+03:00</DOB>
</User>
<User>
<ID>4</ID>
<FirstName>User 4</FirstName>
<LastName>Last Name 4</LastName>
<DOB>1993-11-04T08:16:09.1109688+03:00</DOB>
</User>
</ThisUsers>
</Users>
Good luck.
Upvotes: 1
Reputation: 1407
Your xml has multiple root elements, this is not allowed for valid xml. If you change it to the format, this should work.
<?xml version="1.0"?>
<Users>
<user></user>
<user></user>
</Users>
Upvotes: 1