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
Related
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);
}
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'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 have the following xml:
<Applications>
<AccessibleApplication></AccessibleApplication>
<AccessibleApplication></AccessibleApplication>
<EligibleApplication></EligibleApplication>
<EligibleApplication></EligibleApplication>
</Applications>
Is there a way to deserialize this into a C# object so that the AccessibleApplications and EligibleApplications are two separate arrays? I tried the following but get an exception because "Applications" is used more than once.
[XmlArray("Applications")]
[XmlArrayItem("AccessibleApplication")]
public List<Application> AccessibleApplications { get; set; }
[XmlArray("Applications")]
[XmlArrayItem("EligibleApplication")]
public List<Application> EligibleApplications { get; set; }
The exception is:
The XML element 'Applications' from namespace '' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.
Is this possible to do?
Thanks!
Edit: I forgot to mention that I do not want to have an "Applications" class just for the sake of providing a container object for the two arrays. I have several situations like this and I don't want the clutter of these classes whose only purpose is to split up two arrays of the same type.
I was hoping to be able to deserialize the two arrays into an outer object using some sort of tag like [XmlArrayItem="Application/AccessibleApplication"] without creating an "Applications" class.
I've found a fairly neat way to do this, first make a class like this:
using System.Xml.Serialization;
[XmlRoot]
public class Applications
{
[XmlElement]
public string[] AccessibleApplication;
[XmlElement]
public string[] EligibleApplication;
}
Notice how the elements are individual arrays. Now using this class (I had my XML in a separate file hence the XmlDocument class).
var doc = new XmlDocument();
doc.Load("../../Apps.xml");
var serializer = new XmlSerializer(typeof(Applications));
Applications result;
using (TextReader reader = new StringReader(doc.InnerXml))
{
result = (Applications)serializer.Deserialize(reader);
}
Now to prove this works you can write this all in to a console app and do a foreach to print all the values in your arrays, like so:
foreach (var app in result.AccessibleApplication)
{
Console.WriteLine(app);
}
foreach (var app in result.EligibleApplication)
{
Console.WriteLine(app);
}
You can use XmlElement attribute to deserialize to different lists:
public class Applications
{
[XmlElement("AccessibleApplication")]
public List<Application> AccessibleApplications { get; set; }
[XmlElement("EligibleApplication")]
public List<Application> EligibleApplications { get; set; }
}
public class Application
{
[XmlText]
public string Value { get; set; }
}
So for a sample XML:
<Applications>
<AccessibleApplication>xyz</AccessibleApplication>
<AccessibleApplication>abc</AccessibleApplication>
<EligibleApplication>def</EligibleApplication>
<EligibleApplication>zzz</EligibleApplication>
</Applications>
The following snippet would output the below:
using (var reader = new StreamReader("XMLFile1.xml"))
{
var serializer = new XmlSerializer(typeof(Applications));
var applications = (Applications)serializer.Deserialize(reader);
Console.WriteLine("AccessibleApplications:");
foreach (var app in applications.AccessibleApplications)
{
Console.WriteLine(app.Value);
}
Console.WriteLine();
Console.WriteLine("EligibleApplications:");
foreach (var app in applications.EligibleApplications)
{
Console.WriteLine(app.Value);
}
}
Output:
AccessibleApplications:
xyz
abc
EligibleApplications:
def
zzz
You can use this class to create objects from strings, creating strings from objects and create byte [] from objects
StringToObject
var applicationObject = new XmlSerializerHelper<Applications>().StringToObject(xmlString);
ObjectToString
var xmlString = new XmlSerializerHelper<Applications>().ObjectToString(applicationObject);
ObjectToByteArray
var byteArray = new XmlSerializerHelper<Applications>().ObjectToByteArray(applicationObject);
The XmlSerializerHelper:
namespace StackOverflow
{
public class XmlSerializerHelper<T> where T : class
{
private readonly XmlSerializer _serializer;
public XmlSerializerHelper()
{
_serializer = new XmlSerializer(typeof(T));
}
public T ToObject(string xml)
{
return (T)_serializer.Deserialize(new StringReader(xml));
}
public string ToString(T obj, string encoding)
{
using (var memoryStream = new MemoryStream())
{
_serializer.Serialize(memoryStream, obj);
return Encoding.GetEncoding(encoding).GetString(memoryStream.ToArray());
}
}
public byte[] ToByteArray(T obj, Encoding encoding = null)
{
var settings = GetSettings(encoding);
using (var memoryStream = new MemoryStream())
{
using (var writer = XmlWriter.Create(memoryStream, settings))
{
_serializer.Serialize(writer, obj);
}
return memoryStream.ToArray();
}
}
private XmlWriterSettings GetSettings(Encoding encoding)
{
return new XmlWriterSettings
{
Encoding = encoding ?? Encoding.GetEncoding("ISO-8859-1"),
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
ConformanceLevel = ConformanceLevel.Document
};
}
}
}
Your Class:
[XmlRoot]
public class Applications
{
[XmlElement("AccessibleApplication")]
public string[] AccessibleApplication { get; set; }
[XmlElement("EligibleApplication")]
public string[] EligibleApplication { get; set; }
}
Or
[XmlRoot]
public class Applications
{
[XmlElement("AccessibleApplication")]
public List<string> AccessibleApplication { get; set; }
[XmlElement("EligibleApplication")]
public List<string> EligibleApplication { get; set; }
}
Cheers.
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
);
}
}
}
}