I am new to programming but I need to serialize data, I first read a csv and create classes based on the data in said csv. When I click on a button the List should be serialized for later use.
This is the code for both the Dealership (del) and the Car class:
[DataContract]
public class Dealership
{
[DataMember]
private List<Car> carList = new List<Car>();
public void ImportCars()
{
carList.Clear();
var fileContent = string.Empty;
var filePath = string.Empty;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = "csv files (*.csv)|*.csv";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
filePath = openFileDialog.FileName;
using (var reader = new StreamReader(filePath))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Car
{
Model = csv.GetField("model"),
Brand = csv.GetField("brand"),
Year = Convert.ToInt32(csv.GetField("year")),
Price = Convert.ToDecimal(csv.GetField("price"))
};
carList.Add(record);
}
}
}
}
}
}
[DataContract]
public class Car
{
[DataMember]
private string brand;
[DataMember]
private string model;
[DataMember]
private int year;
[DataMember]
private decimal price;
public string Brand { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public decimal Price { get; set; }
}
This is the form code:
Dealership del1 = del;
FileStream? fs = null;
try
{
fs = new FileStream("dealership.xml", FileMode.OpenOrCreate, FileAccess.Write);
Type mainType = typeof(Dealership);
//List<Type> auxiliryTypes = new List<Type>() { typeof(Car), typeof(Customer) };
DataContractSerializer dcs = new DataContractSerializer(mainType);
dcs.WriteObject(fs, del1);
}
finally
{
if (fs!=null) fs.Close();
}
Snippet of XML output:
<Dealership xmlns="http://schemas.datacontract.org/2004/07/Individual_Assignment" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><carList><Car><brand i:nil="true"/><model i:nil="true"/><price>0</price><year>0</year></Car>
How is it possible for the XML to not contain any values for the cars, when I sell a car by typing customer details in the form it does serialize those values but nothing else.
As a rule of thumb, make your Data Transfer Object (DTO) classes (i.e. classes intended for serialization) have only public properties. You should probably also be using generic collections:
[DataContract]
public class Dealership {
[DataMember]
public List<Car> CarList {get;set;} = new List<Car>();
}
Using public properties tend to work with most serialization libraries. There can be exceptions, some can handle private properties, some can handle immutable types, but then you need to check what specific syntax your library support. I do not remember the exact rules for DataContractSerializer.
Also note that your should probably separate your DTO classes from your domain classes, so you do not have to expose internal fields when actually using your objects, only when communicating with some other part of the system.
I would also advice setting up a unit test to serialize and deserialize your classes. That is an easy way to see what works and what does not. It can also be used to check that old versions of your classes still de serialize correctly if that is a concern for you.
Full example:
[DataContract]
public class Car
{
[DataMember]
public string Brand { get; set; }
[DataMember]
public string Model { get; set; }
[DataMember]
public int Year { get; set; }
[DataMember]
public decimal Price { get; set; }
}
[DataContract]
public class Dealership
{
[DataMember]
public List<Car> Cars { get; set; } = new ();
};
[Test]
public void ShouldSerializeAndDeserialize()
{
var sut = new DataContractXml();
var car = new Car(){Brand = "Volvo", Model = "v70", Price = 20000, Year = 2004};
var dealer = new Dealership();
dealer.Cars.Add(car);
using var ms = new MemoryStream();
var serializer = new DataContractSerializer(typeof(Dealership));
serializer.WriteObject(ms, dealer);
ms.Position = 0;
var result = (Dealership)serializer.ReadObject(ms);
Console.WriteLine(result.Cars[0].Brand);
}
Related
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.
I am trying to load the data that I have serialised into a JSON file. I want my program to work such that the data that was saved, is bought back into the programs memory once the console app is re-opened.
My classes are defined as can be seen below:
public class Student{
public string StudentName { get; set; }
public Dictionary<string, int> GradesRecord = new Dictionary<string, int>();
public Guid StudentId { get; set;
}
public class Subject{
public string SubjectName { get; set; }
public Guid SubjectId { get; set; }
public int MaxMarkAvail;
}
public class ClassOfStudents{
public List<Student> Students = new List<Student>();
public List<Subject> Subjects = new List<Subject>();
public void AddSubject(Subject subjectName){
Subjects.Add(subjectName);
}
public void AddStudent(Student studentName){
Students.Add(studentName);
}
}
I have a method that saves the students grades.
private static void ConvertStudentGradesToJson()
{
var studentsgrades = ClassOfStudents.Students.Select(x => new { x.StudentName, x.StudentId, x.GradesRecord });
JsonSerializer serializer = new JsonSerializer();
serializer.NullValueHandling = NullValueHandling.Ignore;
using (StreamWriter sw = new StreamWriter(#"StudentsGrades.JSON"))
using (JsonWriter writer = new JsonTextWriter(sw))
{
writer.Formatting = Formatting.Indented;
serializer.Serialize(writer, studentsgrades);
}
}
Now, when I close the program, the StudentsGrades.JSON file is created.
I want to know how to load this information back into the programs memory once this program is re-opened after being closed.
NOTE: I am new to JSON. Please do excuse my lack of knowledge as to how JSON works.
Thanks
when reopen app call this method:
public void LoadJson(string filePath="#StudentsGrades.JSON")
{
using (StreamReader r = new StreamReader(filePath))
{
string json = r.ReadToEnd();
List<Students> Students = JsonConvert.DeserializeObject<List<Students>>(json);
}
}
NOTE: you should use Newtonsoft.Json
I'm not sure if I could make my code cleaner by creating a separate class for the process I'm running but I'm doing it this way because it's how I know to do it.
My main objective is to create a JSON file from data collected through HtmlAgilityPack. I've been working with this problem the last couple days but I managed to figure out a way to do it. I managed to create a JSON file with the information retrieved but it didn't divide the information into separate objects in an object array. Instead it clustered up all the data as 1 object.
This was happening because I never created the objects with the parsed html data in the string list. Instead of creating separate lists and combining them, I need to create a list of objects made from the parsed html data and add them to a list.
To test out this hypothetical method I created 3 class instances and gave them values to see if the JSON file created the desired array of objects. When tested, it created the JSON array of objects as desired.
JSON Created:
[{"FavsGTS":"GT1","FavsGPICS":"GP1","FavsRNS":"RN1","FavsPIS":"PI1","FavsIsOns":"true"},
{"FavsGTS":"GT2","FavsGPICS":"GP2","FavsRNS":"RN2","FavsPIS":"PI2","FavsIsOns":"false"},
{"FavsGTS":"GT3","FavsGPICS":"GP3","FavsRNS":"RN3","FavsPIS":"PI3","FavsIsOns":"true"}]
Now I'm trying to figure out how can I dynamically create instances based out of the collected html data.
What I had in mind was doing something like:
gamertagsFav = new List<string>(FavsGTS.Count);
gamertagsFav.AddRange(FavsGTS);
foreach(string gamertagfav in gamertagsFav)
{
//creates a class instance and adds the parsed data in the same order.
}
An example of a generated instance would be as fallows:
gamerprofileFav gpfav1 = new gamerprofileFav()
{
FavsGTS = "gt1",
FavsGPICS = "gpic1",
FavsRNS = "rn1",
FavsPIS = "pi1",
FavsIsOns = "ison1"
};
This is possible because all the parsed data is in the same order.
My code is a bit messy, but it is as fallows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using HtmlAgilityPack;
using System.Web.Script.Serialization;
using Newtonsoft.Json;
using System.IO;
namespace Parser_Test_1._0
{
public partial class Form1 : Form
{
public List<string> FavsGTS { get; private set; }
public List<string> FavsGPICS { get; private set; }
public List<string> FavsRNS { get; private set; }
public List<string> FavsPIS { get; private set; }
public List<string> FavsIsOns { get; private set; }
public List<string> allPlayers { get; private set; }
public List<string> gamertagsFav { get; private set; }
public Form1()
{
InitializeComponent();
}
public class gamerprofileFav
{
public string FavsGTS { get; set; }
public string FavsGPICS { get; set; }
public string FavsRNS { get; set; }
public string FavsPIS { get; set; }
public string FavsIsOns { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.Load(#"C:\Users\josec\Documents\Visual Studio 2015\Projects\THE XBOX PROJECT\Parser-Test-1.0\Parser-Test-1.0\bin\Debug\xbFrSourceCode.txt");
string datacollected1 = doc.DocumentNode.SelectNodes("//*[#id=\"favoritesContent\"]/div[2]/div[2]/ul")[0].InnerHtml;
string datacollected2 = doc.DocumentNode.SelectNodes("//*[#id=\"friendsContent\"]/div[2]/div[2]")[0].InnerHtml;
label1.Text = datacollected1;
label2.Text = datacollected2;
//StreamWriter sw = new StreamWriter("datacollected1.txt");
//sw.Write(datacollected1);
//sw.Close();
//Gamertags
HtmlAgilityPack.HtmlDocument favs = new HtmlAgilityPack.HtmlDocument();
favs.LoadHtml(datacollected1);
FavsGTS = new List<string>();
HtmlNodeCollection gts = favs.DocumentNode.SelectNodes("//li[#data-gamertag]");
foreach (var gt in gts)
{
string datagamertag = gt.Attributes["data-gamertag"].Value;
FavsGTS.Add(datagamertag);
}
listBox1.DataSource = FavsGTS;
FavsGPICS = new List<string>();
HtmlNodeCollection gpics = favs.DocumentNode.SelectNodes("//li/a[1]/img[1][#src]");
foreach (var gpic in gpics)
{
string datagpic= gpic.Attributes["src"].Value;
FavsGPICS.Add(datagpic);
}
listBox2.DataSource = FavsGPICS;
FavsRNS = new List<string>();
HtmlNodeCollection rns = favs.DocumentNode.SelectNodes("//li/div[2]/div[2]/div[1]/div[1]");
foreach (var rn in rns)
{
string datarn = rn.InnerText;
FavsRNS.Add(datarn);
}
listBox3.DataSource = FavsRNS;
FavsPIS = new List<string>();
HtmlNodeCollection pis = favs.DocumentNode.SelectNodes("//li/div[2]/div[2]/div[1]/div[2]");
foreach (var pi in pis)
{
string datapi = pi.InnerText;
FavsPIS.Add(datapi);
}
listBox4.DataSource = FavsPIS;
FavsIsOns = new List<string>();
HtmlNodeCollection isons = favs.DocumentNode.SelectNodes("//li[#data-isonline]");
foreach (var ison in isons)
{
string dataison = ison.Attributes["data-isonline"].Value;
FavsIsOns.Add(dataison);
}
listBox5.DataSource = FavsIsOns;
//Test
gamertagsFav = new List<string>(FavsGTS.Count);
gamertagsFav.AddRange(FavsGTS);
foreach(string gamertagfav in gamertagsFav)
{
}
//Merge
allPlayers = new List<string>(FavsGTS.Count + FavsGPICS.Count + FavsRNS.Count + FavsPIS.Count + FavsIsOns.Count);
allPlayers.AddRange(FavsGTS);
allPlayers.AddRange(FavsGPICS);
allPlayers.AddRange(FavsRNS);
allPlayers.AddRange(FavsPIS);
allPlayers.AddRange(FavsIsOns);
//GpsFav //+Start+
gamerprofileFav gpfav1 = new gamerprofileFav()
{
FavsGTS = "GT1",
FavsGPICS = "GP1",
FavsRNS = "RN1",
FavsPIS = "PI1",
FavsIsOns = "true"
};
gamerprofileFav gpfav2 = new gamerprofileFav()
{
FavsGTS = "GT2",
FavsGPICS = "GP2",
FavsRNS = "RN2",
FavsPIS = "PI2",
FavsIsOns = "false"
};
gamerprofileFav gpfav3 = new gamerprofileFav()
{
FavsGTS = "GT3",
FavsGPICS = "GP3",
FavsRNS = "RN3",
FavsPIS = "PI3",
FavsIsOns = "true"
};
List<gamerprofileFav> gpfavs = new List<gamerprofileFav>();
gpfavs.Add(gpfav1);
gpfavs.Add(gpfav2);
gpfavs.Add(gpfav3);
//GgsFav //-END-
listBox6.DataSource = allPlayers;
listBox7.DataSource = gamertagsFav;
//JSON Serialize
//string data = JsonConvert.SerializeObject(gpfavs);
//File.WriteAllText("data.json", data);
}
public class gpsFav
{
}
}
}
This is the Form1 when run:
The data presented in the 5 small lists is the data that I wish to assign to the generated instances in the same order they appear. That way I can create a list based out of these generated instances which I can serialize to a JSON file.
To avoid doing all of the hard work, there is already an option to deserialize a JSON object into a .NET object that you can work with, an example with your piece of code;
public class RootObject
{
public string FavsGTS { get; set; }
public string FavsGPICS { get; set; }
public string FavsRNS { get; set; }
public string FavsPIS { get; set; }
public string FavsIsOns { get; set; }
}
While you simply deserialize it by;
RootObject gamertag_sample = JsonConvert.DeserializeObject<RootObject>(jsonstr);
Of course if you pass it an array of RootObject, you'll need to replace <RootObject> with <RootObject[]> and so on with the type.
As far as I understood this was the only problem you were seeking for a solution or have I missed something?
EDIT:
With dynamic object you can create any value entry you wish, you'll need to go through a series of tasks to do so before however.
// this would contain your key,value for the generated instance.
// {example_key, "value"} would result later in myObject.example_key (returning "value")
var expandoObj = new ExpandoObject();
var eoCollection = (ICollection<KeyValuePair<string, object>>)expandoObj;
// add your key value pairs here
eoCollection.Add(new KeyValuePair<string, object>("example", "example value"));
dynamic myDynamicObject = expandoObj;
// myDynamicObject.example will return "example value", and would result in json:
// "example":"example value"
I'm trying to save a dictionary of Matrix into an Xml file.
My Matrix class attributes are :
public class Matrix
{
public int Lines { get; set; }
public int Columns { get; set; }
public double[,] Elements { get; set; }
public string name { get; set; }
}
After many attempts, I wrote this :
string fileName = dlg.FileName;
Stream writer = new FileStream(fileName,FileMode.Create);
foreach (KeyValuePair<String, Matrix> matrice in CalMat.Calculatrice.listMatrix)
{
XmlSerializer x = new XmlSerializer(matrice.GetType());
x.Serialize(writer, matrice);
}
writer.Close();
If i run this code with one matrix, the file is created, but i only have this sentence written :
<?xml version="1.0"?><KeyValuePairOfStringMatrix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /><?xml version="1.0"?>
I think my code is missing something but I don't know what. A write method, I guess.
Thank you for your time!
I don't think the default KeyValuePair is serializable,
try building your own KeyValuePair class:
[Serializable]
[XmlType(TypeName="MyTypeName")]
public struct KeyValuePair<T1, T2>
{
public T1 Key { get; set; }
public T2 Value { get; set; }
}
Using BinaryFormatter this is the code:
[Serializable] // mark with Serializable
public class Matrix
{
public Matrix(string name, int lines, int columns)
{
Name = name;
Lines = lines;
Columns = columns;
Elements = new double[Lines, Columns];
}
public int Lines { get; set; }
public int Columns { get; set; }
public double[,] Elements { get; set; }
public string Name { get; set; }
}
public static void Main()
{
var path = #"D:\serialize.data"; // use the path that you want
// this is an example collection
var listMatrix = new Dictionary<string, Matrix>();
listMatrix.Add("matrix_1", new Matrix("Matrix 1", 1, 2));
listMatrix.Add("matrix_2", new Matrix("Matrix 2", 2, 2));
// Serialization
var stream = new FileStream(path, FileMode.Create);
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, listMatrix);
stream.Close();
// Deserialization
stream = new FileStream(path, FileMode.Open);
var result = (Dictionary<string, Matrix>)binaryFormatter.Deserialize(stream);
stream.Close();
}
Using XmlSerializer this is the code:
// I implement my custom KeyValuePair to serialize (because XmlSerializer can not serialize the .net KeyValuePair)
public struct CustomKeyValuePair<T1, T2>
{
public CustomKeyValuePair(T1 key, T2 value): this()
{
Key = key;
Value = value;
}
public T1 Key { get; set; }
public T2 Value { get; set; }
// here I specify how is the cast
public static explicit operator CustomKeyValuePair<T1, T2>(KeyValuePair<T1, T2> keyValuePair)
{
return new CustomKeyValuePair<T1, T2>(keyValuePair.Key, keyValuePair.Value);
}
}
// Matrix class used to Serialize with XmlSerailzer
public class Matrix
{
public Matrix() { } // need a default constructor
public Matrix(string name, int lines, int columns)
{
Name = name;
Lines = lines;
Columns = columns;
Elements = new double[Columns][];
for (int i = 0; i < Elements.Length; i++)
{
Elements[i] = new double[Columns];
}
}
public int Lines { get; set; }
public int Columns { get; set; }
public double[][] Elements { get; set; } // I use double[][] because XmlSerialzer can not serialize a two-dimensional array (double[,])
public string Name { get; set; }
}
public static void Main()
{
var path = #"D:\serialize.data"; // use the path that you want
// this is an example collection
var listMatrix = new Dictionary<string, Matrix>();
listMatrix.Add("matrix_1", new Matrix("Matrix 1", 1, 2));
listMatrix.Add("matrix_2", new Matrix("Matrix 2", 2, 2));
// Serialization
var stream = new FileStream(path, FileMode.Create);
var xmlSerializer = new XmlSerializer(typeof(CustomKeyValuePair<string, Matrix>[]));
var aux = listMatrix.Select(keyValuePair => (CustomKeyValuePair<string, Matrix>) keyValuePair).ToArray();
xmlSerializer.Serialize(stream, aux); // I serialize an array to make easy the deserailizer
stream.Close();
// Deserialization
stream = new FileStream(path, FileMode.Open);
var result = (CustomKeyValuePair<string, Matrix>[])xmlSerializer.Deserialize(stream);
stream.Close();
}
I'm trying to deserialize a rest uri located at http://ws.geonames.org/countryInfo?lang=it&country=DE and keep getting error (There is an error in XML document (1, 1)). Plug http://ws.geonames.org/countryInfo?lang=it&country=DE into the browser and you can see the result.
I have a class
public class Country
{
public string CountryName {get;set;}
public string CountryCode {get;set;}
}
and the method in my console app is as follows:
static void DeserializeTheXML()
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "countryName";
xRoot.IsNullable = true;
XmlSerializer ser = new XmlSerializer(typeof(Country), xRoot);
XmlReader xRdr = XmlReader.Create(new StringReader("http://ws.geonames.org/countryInfo?lang=it&country=DE"));
Country tvd = new Country();
tvd = (Country)ser.Deserialize(xRdr);
Console.WriteLine("Country Name = " + tvd.CountryName);
Console.ReadKey();
}
any ideas on how to deserialize this rest service? thanks..
For serialization to work successfully you need to decorate your objects with the proper serialization attributes or use the XmlAttributeOverrides constructor. Also don't forget that XML is case sensitive and your objects must reflect the XML structure you are deserializing:
public class GeoNames
{
[XmlElement("country")]
public Country[] Countries { get; set; }
}
public class Country
{
[XmlElement("countryName")]
public string CountryName { get; set; }
[XmlElement("countryCode")]
public string CountryCode { get; set; }
}
class Program
{
static void Main()
{
var url = "http://ws.geonames.org/countryInfo?lang=it&country=DE";
var serializer = new XmlSerializer(typeof(GeoNames), new XmlRootAttribute("geonames"));
using (var client = new WebClient())
using (var stream = client.OpenRead(url))
{
var geoNames = (GeoNames)serializer.Deserialize(stream);
foreach (var country in geoNames.Countries)
{
Console.WriteLine(
"code: {0}, name: {1}",
country.CountryCode,
country.CountryName
);
}
}
}
}