How to use XML to store a simple object (C#) - c#

I've been using a file like the following to hold data:
field1 field2 field3 field4
myname myhashedpass myemail#email.com more stuff after
etc etc etc etc
Each line is converted into strings (Name, Pass, Email)
I'd like to get my text file (see above) converted into an XML file, like this:
<person1>
<name>myname</name>
<pass>myhashedpass</pass>
<email>etc</email>
</person1>
<person2>
etc etc etc etc
basically, I'm stuck on how to do this migration and also manipulate the XML data in the same way that I do with the text data

The litteral answer to your question is this:
using System;
using System.Linq;
using System.Xml.Linq;
namespace XmlSerialization
{
class Program
{
static void Main(string[] args)
{
var person1 = new Person();
person1.Name = "Joe";
person1.Password = "Cla$$ified";
person1.Email = "none#your.bussiness";
var person2 = new Person();
person2.Name = "Doe";
person2.Name = "$ecret";
person2.Email = "dont#spam.me";
var persons = new[] {person1, person2};
XElement xml = new XElement("persons",
from person in persons
select new XElement("person",
new XElement("name", person.Name),
new XElement("password", person.Password),
new XElement("email", person.Email))
);
xml.Save("persons.xml");
XElement restored_xml = XElement.Load("persons.xml");
Person[] restored_persons = (from person in restored_xml.Elements("person")
select new Person
{
Name = (string)person.Element("name"),
Password = (string)person.Element("password"),
Email = (string)person.Element("email")
})
.ToArray();
foreach (var person in restored_persons)
{
Console.WriteLine(person.ToString());
}
Console.ReadLine();
}
}
public class Person
{
public string Name { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public override string ToString()
{
return string.Format("The person with name {0} has password {1} and email {2}",
this.Name, this.Password, this.Email);
}
}
}
However, it is much better to let the built-in serializattion classes do the translation to and from XML for you. The code below needs an explicit reference to the System.Runtime.Serialization.dll. The using statement in itself is not enough:
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Runtime.Serialization;
namespace XmlSerialization
{
class Program
{
static void Main(string[] args)
{
var person1 = new Person();
person1.Name = "Joe";
person1.Password = "Cla$$ified";
person1.Email = "none#your.bussiness";
var person2 = new Person();
person2.Name = "Doe";
person2.Name = "$ecret";
person2.Email = "dont#spam.me";
var persons = new[] {person1, person2};
DataContractSerializer serializer=new DataContractSerializer(typeof(Person[]));
using (var stream = new FileStream("persons.xml", FileMode.Create, FileAccess.Write))
{
serializer.WriteObject(stream,persons);
}
Person[] restored_persons;
using (var another_stream=new FileStream("persons.xml",FileMode.Open,FileAccess.Read))
{
restored_persons = serializer.ReadObject(another_stream) as Person[];
}
foreach (var person in restored_persons)
{
Console.WriteLine(person.ToString());
}
Console.ReadLine();
}
}
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Password { get; set; }
[DataMember]
public string Email { get; set; }
public override string ToString()
{
return string.Format("The person with name {0} has password {1} and email {2}",
this.Name, this.Password, this.Email);
}
}
}

so to read out your original file, you have something like:
var people = File.ReadAllLines("filename"))
.Select(line => {
var parts = line.Split();
return new Person {
Name = parts[0],
Password = parts[1],
Email = parts[2]
});
then you can write out to xml by:
var serializer = new XmlSerializer(typeof(Person));
var xmlfile = File.OpenWrite("somefile");
foreach(var person in people)
serializer.Serialize(person, xmlfile);

You may want to take a look at this XML Serialization tutorial. Serialization can save you a lot of work loading and saving the XML file.

Linq provides a nice way to construct XML using the XNodes:
from p in person
select new XElement("person",
from s in p.Keys
select new XElement(s, p[s]));
Easy as cake.

It's not exactly clear from your question, but it sounds like you are serializing a Person class to a text file. This is probably the perfect use case for the XmlSerializer.
Example code:
class Person
{
// XmlSerializer requires parameterless constructor
public Person()
{
}
public string Name { get; set; }
public string Pass { get; set; }
public string Email { get; set; }
public string Host { get; set; }
}
// ...
XmlSerializer serializer = new XmlSerializer(typeof(Person));
// Write a person to an XML file
Person person = new Person() { Name = "N", Pass = "P", Email = "E", Host = "H" };
using (XmlWriter writer = XmlWriter.Create("person.xml"))
{
serializer.Serialize(writer);
}
// Read a person from an XML file
using (XmlReader reader = XmlReader.Create("person.xml"))
{
person = (Person)serializer.Deserialize(reader);
}

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

difference between XmlSerializerOutputFormatter and XmlDataContractSerializerOutputFormatter

I am wondering what is the difference between these two serializers. when setting accept header = application/xml. Am using Plain DTOs as return values, Which one is preferred? Also consumer of the api who request xml in response which should be used?
Am working on aspnet core web api 3.1, building restful apis. Any suggestions/redirects on the above query will be helpful.
The XmlSerializerOutputFormatter is an asp.net core outputformatter that uses the XmlSerializer internally, whereas the DataContractSerializerOutputFormatter uses the DataContractSerializer internally.
The DataContractSerializer is more flexible in configuration. For example it supports reference detection to prevent the serializer from recursively serializing items, which would normally cause an endless loop.
In my own projects, I prefer to use the DataContractSerializerOutputFormatter because it's able to cope with properties with private setter
public string Text { get; private set; }
Failing case
Dtos project
namespace DataContractSerializerPOC.Dtos
{
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Fullname can only be set from this project
public string FullName { get; internal set; }
}
public class PersonService
{
public List<Person> GetPeople()
{
// Create a list of people to be serialized
var people = new List<Person>
{
new Person { Id = 1, FirstName = "John", LastName = "Doe" },
new Person { Id = 2, FirstName = "Jim", LastName = "Flix" },
new Person { Id = 3, FirstName = "Jack", LastName = "Splick" },
};
// Set the fullname from this project
// In some cases you may need to do this, instead of implementing a readonly property
foreach (var person in people)
person.FullName = $"{person.FirstName} {person.LastName}";
return people;
}
}
}
Console project
namespace DataContractSerializerPOC
{
class Program
{
static void Main(string[] args)
{
var personService = new PersonService();
var people = personService.GetPeople();
var writer = new StringWriter();
var serializer = new XmlSerializer(typeof(List<Person>));
serializer.Serialize(writer, people);
}
}
}
Result
Working case with DataContractSerializer
Dtos project
namespace DataContractSerializerPOC.Dtos
{
[DataContract]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
// Fullname can only be set from this project
[DataMember]
public string FullName { get; internal set; }
}
public class PersonService
{
...
}
}
Console project
namespace DataContractSerializerPOC
{
class Program
{
static void Main(string[] args)
{
var personService = new PersonService();
var people = personService.GetPeople();
var memoryStream = new MemoryStream();
var serializer = new DataContractSerializer(typeof(List<Person>));
serializer.WriteObject(memoryStream, people);
memoryStream.Seek(0, SeekOrigin.Begin);
var text = new StreamReader(memoryStream).ReadToEnd();
}
}
}
Result
So the DataContractSerializer is able to deal with properties with a private setter, whilst the XmlSerializer isn't.

How can I write headers with spaces using CsvHelper?

I am using the CsvHelper library to generate a CSV file from an IEnumerable<Person>, where Person is a basic class.
public class Person
{
public string DisplayName { get; set; }
public int Age { get; set; }
}
I need to write quoted headers, i.e. instead of "DisplayName", the column in the resulting file should be "Display Name".
How can I do this?
Create a ClassMap.
void Main()
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvWriter(writer))
{
var records = new List<Person>
{
new Test { DisplayName = "one", Age = 1},
new Test { DisplayName = "two", Age = 2 },
};
csv.Configuration.RegisterClassMap<PersonMap>();
csv.WriteRecords(records);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
public class Person
{
public string DisplayName { get; set; }
public int Age { get; set; }
}
public sealed class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Map(m => m.DisplayName).Name("Display Name");
Map(m => m.Age);
}
}
Output:
Display Name,Age
one,1
two,2
I had no headers with workaround, so I did this quick workaround instead:
foreach (var property in typeof(MyCsvClass).GetProperties())
{
csvWriter.WriteField(property.Name.Replace('_', ' '));
}
csvWriter.NextRecord();
csvWriter.WriteRecords(models);
This takes the property names and replace underscore with space, so I could just name the properties with underscores instead of spaces, and it mapped correctly.
It requires, however, that you use the HasHeaderRecord = false option on the CsvWriter.

Creating Xml Dynamically From Object

Here I have a scenario that Create xml file dynamically and it should be serialisable. xml is like:
<person>
<personaldata>
<name>gopi</name>
<lastname>ch</lastname>
</personaladata>
<Educationaladata>
<Graduation>b.tech</graduation>
<designation>Engineer</designation>
</educationaldata>
</person>
person class has name, lastname, designation, graduation and properties
I tried this
public string CreateXmlObject(Person objPerson)
{
var objXmlDocument = new XmlDocument();
var objXpath = objXmlDocument.CreateNavigator();
var objXmlSeialiser = new XmlSerializer(objPerson.GetType());
using (var xs = objXpath.AppendChild())
{
objXmlSeialiser.Serialize(xs, objPerson);
}
return objXmlDocument.OuterXml;
}
My Problem is I have to read Specific data from Xml And Update Specific data to Xml . I want to read only Personaldata when i update, update should only apply to Personaldata Not Otherdata
Try xml linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string name = "gopi";
string lastname = "ch";
string graduation = "b.tech";
string designation = "Engineer";
XElement personalData = new XElement("person", new XElement[] {
new XElement("personaldata", new XElement[] {
new XElement("name", name),
new XElement("lastname", lastname)
}),
new XElement("Educationadata", new XElement[] {
new XElement("Graduation", graduation),
new XElement("designation", designation)
})
});
}
}
}
​
Fisrt of all your XML is invalid, all the nodes should match their closing tags. Considering your person class looks something like this:-
public class Person
{
public string name { get; set; }
public string lastname { get; set; }
public string Graduation { get; set; }
public string designation { get; set; }
}
You can easily do it with LINQ-to-XML:-
Xdocument = XDocument.Load("XMLFilePath");
List<Person> persons = (from person in xdoc.Descendants("person")
let personaldata = person.Element("personaldata")
let Educationaladata = person.Element("Educationaladata")
select new Person
{
name = (string)personaldata.Element("name"),
lastname = (string)personaldata.Element("lastname"),
Graduation = (string)Educationaladata.Element("Graduation"),
designation = (string)Educationaladata.Element("designation")
}).ToList();
sSometing like -
var xml = XDocument.Load("xml path");
var personaldata = xml.Descendents("personaldata").FirstOrDefault();
if (data != null)
{
foreach (var t in data.Descendants())
{
t.Value = "test";
}
}

How can I serialize an object with a Dictionary<string,object> property?

In the example code below, I get this error:
Element
TestSerializeDictionary123.Customer.CustomProperties
vom Typ
System.Collections.Generic.Dictionary`2[[System.String,
mscorlib, Version=2.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089],[System.Object,
mscorlib, Version=2.0.0.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089]] can
not be serialized because it
implements IDictionary.
When I take out the Dictionary property, it works fine.
How can I serialize this Customer object with the dictionary property? Or what replacement type for Dictionary can I use that would be serializable?
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.Text;
namespace TestSerializeDictionary123
{
public class Program
{
static void Main(string[] args)
{
List<Customer> customers = Customer.GetCustomers();
Console.WriteLine("--- Serializing ------------------");
foreach (var customer in customers)
{
Console.WriteLine("Serializing " + customer.GetFullName() + "...");
string xml = XmlHelpers.SerializeObject<Customer>(customer);
Console.WriteLine(xml);
Console.WriteLine("Deserializing ...");
Customer customer2 = XmlHelpers.DeserializeObject<Customer>(xml);
Console.WriteLine(customer2.GetFullName());
Console.WriteLine("---");
}
Console.ReadLine();
}
}
public static class StringHelpers
{
public static String UTF8ByteArrayToString(Byte[] characters)
{
UTF8Encoding encoding = new UTF8Encoding();
String constructedString = encoding.GetString(characters);
return (constructedString);
}
public static Byte[] StringToUTF8ByteArray(String pXmlString)
{
UTF8Encoding encoding = new UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(pXmlString);
return byteArray;
}
}
public static class XmlHelpers
{
public static string SerializeObject<T>(object o)
{
MemoryStream ms = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8);
xs.Serialize(xtw, o);
ms = (MemoryStream)xtw.BaseStream;
return StringHelpers.UTF8ByteArrayToString(ms.ToArray());
}
public static T DeserializeObject<T>(string xml)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
MemoryStream ms = new MemoryStream(StringHelpers.StringToUTF8ByteArray(xml));
XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8);
return (T)xs.Deserialize(ms);
}
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Street { get; set; }
public string Location { get; set; }
public string ZipCode { get; set; }
public Dictionary<string,object> CustomProperties { get; set; }
private int internalValue = 23;
public static List<Customer> GetCustomers()
{
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { Id = 1, FirstName = "Jim", LastName = "Jones", ZipCode = "23434" });
customers.Add(new Customer { Id = 2, FirstName = "Joe", LastName = "Adams", ZipCode = "12312" });
customers.Add(new Customer { Id = 3, FirstName = "Jack", LastName = "Johnson", ZipCode = "23111" });
customers.Add(new Customer { Id = 4, FirstName = "Angie", LastName = "Reckar", ZipCode = "54343" });
customers.Add(new Customer { Id = 5, FirstName = "Henry", LastName = "Anderson", ZipCode = "16623" });
return customers;
}
public string GetFullName()
{
return FirstName + " " + LastName + "(" + internalValue + ")";
}
}
}
In our application we ended up using:
DataContractSerializer xs = new DataContractSerializer(typeof (T));
instead of:
XmlSerializer xs = new XmlSerializer(typeof (T));
which solved the problem as DatacontractSerializer supports Dictionary.
Another solution is ths XML Serializable Generic Dictionary workaround also works in the above example, and there is a long discussion at that link from people using it, might be useful for people working with this issue.
Here's a generic dictionary class that knows how to serialize itself:
public class XmlDictionary<T, V> : Dictionary<T, V>, IXmlSerializable {
[XmlType("Entry")]
public struct Entry {
public Entry(T key, V value) : this() { Key = key; Value = value; }
[XmlElement("Key")]
public T Key { get; set; }
[XmlElement("Value")]
public V Value { get; set; }
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() {
return null;
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) {
this.Clear();
var serializer = new XmlSerializer(typeof(List<Entry>));
reader.Read(); // Why is this necessary?
var list = (List<Entry>)serializer.Deserialize(reader);
foreach (var entry in list) this.Add(entry.Key, entry.Value);
reader.ReadEndElement();
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) {
var list = new List<Entry>(this.Count);
foreach (var entry in this) list.Add(new Entry(entry.Key, entry.Value));
XmlSerializer serializer = new XmlSerializer(list.GetType());
serializer.Serialize(writer, list);
}
}
You can't (short of doing it all yourself, which is horrible); the xml serializer isn't going to have a clue what to do with object, as it doesn't include type metadata in the wire format. One (hacky) option would be to stream these all as strings for the purposes of serialization, but then you have a lot of extra parsing (etc) code to write.
You can use Binary serialization instead. (Just make sure all your classes are marked as [Serializable]. Of course, it won't be in XML format, but you didn't list that as a requirement :)
I've just found this blog post by Rakesh Rajan which describes one possible solution:
Override XmlSerialization by making the type implement the System.Xml.Serialization.IXmlSerializable class. Define how you want the object to be serialized in XML in the WriteXml method, and define how you could recreate the object from an xml string in the ReadXml method.
But this wouldn't work as your Dictionary contains an object rather than a specific type.
What about to mark Customer class as DataContract and its properties as DataMembers. DataContract serializer will do the serialization for you.
Try Serializating through BinaryFormatter
private void Deserialize()
{
try
{
var f_fileStream = File.OpenRead(#"dictionarySerialized.xml");
var f_binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
myDictionary = (Dictionary<string, myClass>)f_binaryFormatter.Deserialize(f_fileStream);
f_fileStream.Close();
}
catch (Exception ex)
{
;
}
}
private void Serialize()
{
try
{
var f_fileStream = new FileStream(#"dictionarySerialized.xml", FileMode.Create, FileAccess.Write);
var f_binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
f_binaryFormatter.Serialize(f_fileStream, myDictionary);
f_fileStream.Close();
}
catch (Exception ex)
{
;
}
}

Categories