XML serialize without overwriting - c#

How can I serialize data to xml without overwriting previous data?
In this code I am able to make XML file but when I run this again I overwrite what I saved previously:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml.Serialization;
namespace ActorGenerator
{
class Program
{
static void Main()
{
Console.Write("How many actors you want to add? ");
int max = 0;
max = int.Parse(Console.ReadLine());
for (int i = 0; i < max; i++)
{
int x = i + 1;
Console.WriteLine("////////////////////////ACTOR" + x + "/////////////////////////////////");
Actor actor1 = new Actor();
Console.Write("NAME (string): "); actor1.Name = Console.ReadLine();
Console.Write("AGE (int): "); actor1.Age = int.Parse(Console.ReadLine());
Console.Write("FOCUS (string): "); actor1.Focus = Console.ReadLine();
Console.Write("PRICE (int): "); actor1.Price = int.Parse(Console.ReadLine());
Console.Write("CONTRACTED (true/false): "); actor1.Contracted = bool.Parse(Console.ReadLine());
Console.Write("PLAYING IN FILM (true/false): "); actor1.PlayingInFilm = bool.Parse(Console.ReadLine());
Console.Write("SKILL (int): "); actor1.Skill = int.Parse(Console.ReadLine());
SerializeToXML(actor1);
}
}
static public void SerializeToXML(Actor actor)
{
XmlSerializer serializer = new XmlSerializer(typeof(Actor));
TextWriter textWriter = new StreamWriter(#"c:\users\Desktop\actor.xml");
serializer.Serialize(textWriter, actor);
textWriter.Close();
}
}
public class Actor
{
public string Name { get; set; }
public int Age { get; set; }
public string Focus { get; set; }
public int Price { get; set; }
public bool Contracted { get; set; }
public bool PlayingInFilm { get; set; }
public int Skill { get; set; }
}
}
Also, how can I read XML data and store it into variable?

It sounds like you should create a List<Actor> - you can then start with a list of one entry, serialize that list, then next time round deserialize the list, add an actor, and then serialize again so you'll have two entries in the list the following time, etc.

To read the data, load the file to a StreamReader, and then use the Deserialize method on the serializer:
XmlSerializer serializer = new XmlSerializer(typeof(Actor));
TextReader textReader = new StreamReader(#"c:\users\Desktop\actor.xml");
Actor actor = (Actor)serializer.Deserialize(textReader);
textReader.Close();
(Assuming you only have one actor you're saving in the XML file)
To not overwrite, I recommend you read the data, as described above, and then update the object, and then finally write to the XML file. This way you preserve what was originally in the XML file as desired.
(Assuming you want many actors in the XML file)
You likely want to serialize a List rather than just Actor. If so, you can still read the data in (cast to List instead), update the list as necessary, then serialize back out to the XML file.
PS: Instead of using "Close" on your readers/writers, and other IDisposable objects, I recommend you use the "using" statement in C#.

Just pass a true into your constructor for the StreamWriter
static public void SerializeToXML(Actor actor)
{
XmlSerializer serializer = new XmlSerializer(typeof(Actor));
TextWriter textWriter = new StreamWriter(#"c:\users\Desktop\actor.xml",true);
serializer.Serialize(textWriter, actor);
textWriter.Close();
}
As far as fixing the Xml, you could use Linq To XML http://blogs.msdn.com/b/wriju/archive/2008/02/18/linq-to-xml-creating-complex-xml-through-linq.aspx to create valid XML and save it to file in one shot

Related

Serialization: Dynamic class names

I have already tried various possibilities but maybe I am just too tired of seeing the solution -.-
I have an xml structure like this:
<diagnosisList>
<diagnosis>
<surgery1>
<date>1957-08-13</date>
<description>a</description>
<ops301>0-000</ops301>
</surgery1>
<surgery2>
<date>1957-08-13</date>
<description>a</description>
<ops301>0-000</ops301>
</surgery2>
<surgery...>
</surgery...>
</diagnosis>
</diagnosisList>
As you see there is a variable number of surgeries. I have a class "surgery" containing the XML elements.
class Surgery
{
[XmlElement("date")]
public string date { get; set; }
[XmlElement("description")]
public string description { get; set; }
[XmlElement("ops301")]
public string ops301 { get; set; }
public Surgery()
{
}
}
and a class diagnosis creating the structure by adding the surgery class to the constructor.
diagnosis.cs
class Diagnosis
{
[XmlElement("surgery")]
public Surgery surgery
{
get;
set;
}
public Diagnosis(Surgery Surgery)
{
surgery = Surgery;
}
}
I need to be able to serialize the class name of the surgery dynamically by adding a number before serialization happens.
does anybody know a way to achieve that?
any help is really appreciated :)
Kind regards
Sandro
-- EDIT
I create the whole structure starting from my root class "Import". this class then will be passed to the serializer. So I cannot use XMLWriter in the middle of creation of the structure. I Need to create the whole structure first and finally it will be serialized:
private static void XmlFileSerialization(Import import)
{
string filename = #"c:\dump\trauma.xml";
// default file serialization
XmlSerializer<Import>.SerializeToFile(import, filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
settings.IndentChars = "\t";
XmlSerializer<Import>.SerializeToFile(import, filename, namespaces, settings);
}
and then in the Method "SerializeToFile"
public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces, XmlWriterSettings settings)
{
if (source == null)
throw new ArgumentNullException("source", "Object to serialize cannot be null");
XmlSerializer serializer = new XmlSerializer(source.GetType());
using (XmlWriter xmlWriter = XmlWriter.Create(filename, settings))
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T));
x.Serialize(xmlWriter, source, namespaces);
}
}
}
What I Need is to be able to instantiate a variable number of classes based on the main class "Surgery". The class must have a variable Name, i.e.
surgery1, surgery2, surgery3, etc.
This cannot be changed because this is given by the Institution defining the XML structure.
the class must be accessible by its dynamic Name because the property in the class must be set.
so:
surgery1.Property = "blabla";
surgery2. Property = "babla";
etc.
I am even thinking about using T4 methods to create this part of code, but there must be another way to achieve dynamic class names.
I also thought of creating instances with variable names of the class by using reflection:
System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(string className)
But this doesn't work actually -.-
Does anybody have a hint and could put me in the right direction?
I think, you could try implement methods from IXmlSerializable in object contains diagnosisList.
Try to use custom xml writer and reader.
public class SurgeryWriter : XmlTextWriter
{
public SurgeryWriter(string url) : base(url, Encoding.UTF8) { }
private int counter = 1;
public override void WriteStartElement(string prefix, string localName, string ns)
{
if (localName == "surgery")
{
base.WriteStartElement(prefix, "surgery" + counter, ns);
counter++;
}
else
base.WriteStartElement(prefix, localName, ns);
}
}
public class SurgeryReader : XmlTextReader
{
public SurgeryReader(string url) : base(url) { }
public override string LocalName
{
get
{
if (base.LocalName.StartsWith("surgery"))
return "surgery";
return base.LocalName;
}
}
}
Classes:
[XmlRoot("diagnosisList")]
public class DiagnosisList
{
[XmlArray("diagnosis")]
[XmlArrayItem("surgery")]
public Surgery[] Diagnosis { get; set; }
}
[XmlRoot("surgery")]
public class Surgery
{
[XmlElement("date", DataType = "date")]
public DateTime Date { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("ops301")]
public string Ops301 { get; set; }
}
Use:
var xs = new XmlSerializer(typeof(DiagnosisList));
DiagnosisList diagnosisList;
using (var reader = new SurgeryReader("test.xml"))
diagnosisList = (DiagnosisList)xs.Deserialize(reader);
using (var writer = new SurgeryWriter("test2.xml"))
xs.Serialize(writer, diagnosisList);
Don't mix XML and C#.
You don't need dynamic names in the C# code!
If you need an arbitrary number of instances of a class, create them in a loop and place it in any collection.
var surgeries = new List<Surgery>();
for (int i = 0; i < 10; i++)
{
var surgery = new Surgery();
surgeries.Add(surgery);
}
Later you can access them by index or by enumerating.
surgeries[5]
foreach (var surgery in surgeries)
{
// use surgery
}
As you can see no need dynamic names!
Alternatively, use the dictionary with arbitrary names as keys.
var surgeryDict = new Dictionary<string, Surgery>();
for (int i = 0; i < 10; i++)
{
var surgery = new Surgery();
surgeryDict["surgery" + i] = surgery;
}
Access by name:
surgeryDict["surgery5"]

Calling a method that expects an array of objects

I'm learning C# and have written a console program to save an an array of high scores to a file. Although the program works, how I have got it to work is making me feel uneasy and feels more of a hack than a solution so I was looking for guidance on how I should have written it.
What I am currently doing within the Main method is:
Declaring an array of highscore objects
Initialising them
Assigning some values to the array.
I am happy with what I have done up until now, it's the following two steps that make me uneasy
I then declare another HighScore object
I use this object to pass the array of highscores to the SaveHighScores method.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace HighScore
{
class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
public void SaveHighScores(HighScore[] highScores)
{
string allHighScoresText = "";
foreach (HighScore score in highScores)
{
allHighScoresText += $"{score.Name},{score.Score}" + Environment.NewLine;
}
File.WriteAllText("C:/Temp/highscores.csv", allHighScoresText);
}
static void Main(string[] args)
{
HighScore[] highScore = new HighScore[2];
for (int i = 0; i < highScore.Length; i++)
{
highScore[i] = new HighScore();
}
highScore[0].Name = "A";
highScore[0].Score = 100;
highScore[1].Name = "B";
highScore[1].Score = 200;
// are the following two lines correct or am I missing something?
HighScore hs = new HighScore();
hs.SaveHighScores(highScore);
}
}
}
Make SaveHighScores static and you won't need an instance of HighScore to call it. (You can call it directly as HighScore.SaveHighScores())
I prefer to split the representation of your data from the actions that you perform on this data. So I would go for two classes, one for the Data and one for the Save/Load and other business logic
public class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
}
// This class handles the core work to persist your data on the storage medium
// The class is static so you don't need to declare instances and use directly the methods available.
public static class Repo_HighScore
{
// For simplicity, no error Handling but, for a robust implementation,
// error handling is required
public static bool SaveHighScores(HighScore[] highScores)
{
StringBuilder allHighScoresText = new StringBuilder();
foreach (HighScore score in highScores)
allHighScoresText.AppendLine($"{score.Name},{score.Score}");
File.WriteAllText("C:/Temp/highscores.csv", allHighScoresText.ToString());
}
public static HighScore[] LoadHighScores()
{
List<HighScore> hs = new List<HighScore>();
foreach(string line in File.ReadLines("C:/Temp/highscores.csv"))
{
string[] parts = line.Split(',');
HighScore temp = new HighScore()
{ Name = parts[0], Score = Convert.ToInt32(parts[1])};
hs.Add(temp);
}
return hs.ToArray();
}
}

Using XMLReader to read large XML documents to parse the information into a class

I have been using XDocument combined with LINQ to XML to load in xml files and populate my class.
But now I am tasked with making sure my program can handle all sizes of XML documents which means i need to use XML Reader and at this time being i cant get my head around manipulating the XMLReader to populate my class.
currently i have the below class to populate:
public class DataRecord
{
private List<Fields> field = new List<Fields>();
public string ID { get; set; }
public string TotalLength { get; set; }
public List<Fields> MyProperty
{
get { return field; }
set { field = value; }
}
}
internal class Fields
{
public string name { get; set; }
public string startByte { get; set; }
public string length { get; set; }
}
}
I have been trying to switch statements to enforce the xmlreader to provide the data from me to populate the class. For example:
using (XmlReader reader = XmlReader.Create(filename))
{
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.Name)
{
case "DataRecord":
var dataaa = new dataclass.DataRecord();
break;
}
break;
}
}
}
But as i said this is an example, I have searched for ages to try and find an answer but I am getting confused. Hopefully someone can help we my problem.
You can use XmlReader to move through the document, but then load each element using XElement.
Here's a short example:
using System;
using System.Xml;
using System.Xml.Linq;
class Test
{
static void Main()
{
using (var reader = XmlReader.Create("test.xml"))
{
while (reader.ReadToFollowing("foo"))
{
XElement element = XElement.Load(reader.ReadSubtree());
Console.WriteLine("Title: {0}", element.Attribute("title").Value);
}
}
}
}
With sample XML:
<data>
<foo title="x" /><foo title="y">asd</foo> <foo title="z" />
</data>
(Slightly inconsistent just to show that it can handle elements with content, elements with no space between them, and elements with space between them.)
Then obviously in the loop you'd do whatever you need to with the XElement - if you've already got a way of creating an instance of your class from an XElement, you can just call that, use the object, and you're away.

Read and parse a Json File in C#

How does one read a very large JSON file into an array in c# to be split up for later processing?
I have managed to get something working that will:
Read the file Miss out headers and only read values into array.
Place a certain amount of values on each line of an array. (So I
could later split it an put into 2d array)
This was done with the code below but it crashes the program after entering a few lines into the array. This might have to do with the file size.
// If the file extension was a jave file the following
// load method will be use else it will move on to the
// next else if statement
if (fileExtension == ".json")
{
int count = 0;
int count2 = 0;
int inOrOut = 0;
int nRecords=1;
JsonTextReader reader = new JsonTextReader(new StreamReader(txtLoaction.Text));
string[] rawData = new string[5];
while (reader.Read())
{
if (reader.Value != null)
if (inOrOut == 1)
{
if (count == 6)
{
nRecords++;
Array.Resize(ref rawData, nRecords);
//textBox1.Text += "\r\n";
count = 0;
}
rawData[count2] += reader.Value + ","; //+"\r\n"
inOrOut = 0;
count++;
if (count2 == 500)
{
MessageBox.Show(rawData[499]);
}
}
else
{
inOrOut = 1;
}
}
}
A snippet of the JSON I am working with is:
[
{ "millis": "1000",
"stamp": "1273010254",
"datetime": "2010/5/4 21:57:34",
"light": "333",
"temp": "78.32",
"vcc": "3.54" },
]
I need the values out of this JSON. For example, I need "3.54", but I would not want it to print the "vcc".
How can one read a JSON file in and only extract the data needed to be put into an array?
How about making everything easier with Json.NET?
public void LoadJson()
{
using (StreamReader r = new StreamReader("file.json"))
{
string json = r.ReadToEnd();
List<Item> items = JsonConvert.DeserializeObject<List<Item>>(json);
}
}
public class Item
{
public int millis;
public string stamp;
public DateTime datetime;
public string light;
public float temp;
public float vcc;
}
You can even get the values dynamically without declaring Item class.
dynamic array = JsonConvert.DeserializeObject(json);
foreach(var item in array)
{
Console.WriteLine("{0} {1}", item.temp, item.vcc);
}
Doing this yourself is an awful idea. Use Json.NET. It has already solved the problem better than most programmers could if they were given months on end to work on it. As for your specific needs, parsing into arrays and such, check the documentation, particularly on JsonTextReader. Basically, Json.NET handles JSON arrays natively and will parse them into strings, ints, or whatever the type happens to be without prompting from you. Here is a direct link to the basic code usages for both the reader and the writer, so you can have that open in a spare window while you're learning to work with this.
This is for the best: Be lazy this time and use a library so you solve this common problem forever.
Answer for .NET Core
You can just use the built-in System.Text.Json instead of the 3rd-party Json.NET. To promote reuse, the JSON-file-reading functionality belongs in its own class and should be generic rather than hard-coded to a certain type (Item). Here's a full example:
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
namespace Project
{
class Program
{
static async Task Main()
{
Item item = await JsonFileReader.ReadAsync<Item>(#"C:\myFile.json");
}
}
public static class JsonFileReader
{
public static async Task<T> ReadAsync<T>(string filePath)
{
using FileStream stream = File.OpenRead(filePath);
return await JsonSerializer.DeserializeAsync<T>(stream);
}
}
public class Item
{
public int millis;
public string stamp;
public DateTime datetime;
public string light;
public float temp;
public float vcc;
}
}
Or, if you prefer something simpler/synchronous:
class Program
{
static void Main()
{
Item item = JsonFileReader.Read<Item>(#"C:\myFile.json");
}
}
public static class JsonFileReader
{
public static T Read<T>(string filePath)
{
string text = File.ReadAllText(filePath);
return JsonSerializer.Deserialize<T>(text);
}
}
This can also be done in the following way:
JObject data = JObject.Parse(File.ReadAllText(MyFilePath));
string jsonFilePath = #"C:\MyFolder\myFile.json";
string json = File.ReadAllText(jsonFilePath);
Dictionary<string, object> json_Dictionary = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(json);
foreach (var item in json_Dictionary)
{
// parse here
}
Based on #L.B.'s solution, the (typed as Object rather than Anonymous) VB code is
Dim oJson As Object = JsonConvert.DeserializeObject(File.ReadAllText(MyFilePath))
I should mention that this is quick and useful for constructing HTTP call content where the type isn't required. And using Object rather than Anonymous means you can maintain Option Strict On in your Visual Studio environment - I hate turning that off.
For any of the JSON parse, use the website http://json2csharp.com/ (easiest way) to convert your JSON into C# class to deserialize your JSON into C# object.
public class JSONClass
{
public string name { get; set; }
public string url { get; set; }
public bool visibility { get; set; }
public string idField { get; set; }
public bool defaultEvents { get; set; }
public string type { get; set; }
}
Then use the JavaScriptSerializer (from System.Web.Script.Serialization), in case you don't want any third party DLL like newtonsoft.
using (StreamReader r = new StreamReader("jsonfile.json"))
{
string json = r.ReadToEnd();
JavaScriptSerializer jss = new JavaScriptSerializer();
var Items = jss.Deserialize<JSONClass>(json);
}
Then you can get your object with Items.name or Items.Url etc.
Very Easiest way I found on online to work with .JSON file in C#(or any other Programming Language)
Prerequisite:-
Install Newtonsoft.Json Library into your Project
Newtonsoft.Json
and here is the URL -> https://app.quicktype.io/
Steps
1> go to this URL - https://app.quicktype.io/
2> Copy and Paste your JSON file structure into Left sidebar
app.quicktype.io
3> Select required Language (here C#) from Options menu
4> Copy generated code and go to your Project and Create a new .cs file with the same name(here "Welcome.cs")
Welcome.cs
5> Paste all generated code into the newly created class.
Welcome.cs pasted Code
6> that's it. :)
Steps to Access value
1> Go to Main Program .cs file or wherever you need to access it.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Access Json values using Keys.>");
String jsonString = new StreamReader("give <.json> file Path here").ReadToEnd();
// use below syntax to access JSON file
var jsonFile = Welcome.FromJson(jsonString);
string FileName = jsonFile.File;
long Lvl = jsonFile.Level;
bool isTrue = jsonFile.CSharp;
Console.WriteLine(FileName);//JSON
Console.WriteLine(Lvl);//1
Console.WriteLine(isTrue);//true
}
}
For finding the right path I'm using
var pathToJson = Path.Combine("my","path","config","default.Business.Area.json");
var r = new StreamReader(pathToJson);
var myJson = r.ReadToEnd();
// my/path/config/default.Business.Area.json
[...] do parsing here
Path.Combine uses the Path.PathSeparator and it checks whether the first path has already a separator at the end so it will not duplicate the separators. Additionally, it checks whether the path elements to combine have invalid chars.
See https://stackoverflow.com/a/32071002/4420355
There is a faster way of parsing json then Json.Net . If you are using .net core 3.0 or up then you can use the System.Text.Json nuget package to serialize or deserialize.
you need to add:
using System.Text.Json
And then you can serialize as:
var jsonStr = JsonSerializer.Serialize(model);
And Deserialize as:
var model = JsonSerializer.Deserialize(jsonStr);
This code can help you:
string _filePath = Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory);
JObject data = JObject.Parse(_filePath );
There is an easier way to get JSON from file or from the Web:
Json.Net.Curl
Install-Package Json.Net.Curl
// get JObject from local file system
var json = Json.Net.Curl.Get(#"data\JObjectUnitTest1.json");
var json = await Json.Net.Curl.GetAsync(#"data\JObjectUnitTest1.json")
// get JObject from Server
var json = await Json.Net.Curl.GetAsync("http://myserver.com/data.json");
GitHub Project
Nuget
With Cinchoo ETL, an open source library, parsing of very large JSON file is iterative and simple to use
1. Dynamic Method: - No POCO class required
string json = #"
[
{
""millis"": ""1000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
},
{
""millis"": ""2000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
}
]
";
using (var r = ChoJSONReader.LoadText(json))
{
foreach (var rec in r)
Console.WriteLine(rec.Dump());
}
Sample fiddle: https://dotnetfiddle.net/mo1qvw
2. POCO Method:
Define POCO class matching json attributes
public class Item
{
public int Millis { get; set; }
public string Stamp { get; set; }
public DateTime Datetime { get; set; }
public string Light { get; set; }
public float Temp { get; set; }
public float Vcc { get; set; }
}
Then using the parser to load the JSON as below
string json = #"
[
{
""millis"": ""1000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
},
{
""millis"": ""2000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
}
]
";
using (var r = ChoJSONReader<Item>.LoadText(json))
{
foreach (var rec in r)
Console.WriteLine(ChoUtility.Dump(rec));
}
Sample fiddle: https://dotnetfiddle.net/fRWu0w
Disclaimer: I'm author of this library.

Serialization and Deserialization into an XML file, C#

I have the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication28
{
class Program
{
static void Main()
{
List<string> dirs = FileHelper.GetFilesRecursive(#"c:\Documents and Settings\bob.smith\Desktop\Test");
foreach (string p in dirs)
{
Console.WriteLine(p);
}
//Write Count
Console.WriteLine("Count: {0}", dirs.Count);
Console.Read();
}
static class FileHelper
{
public static List<string> GetFilesRecursive(string b)
{
// 1.
// Store results in the file results list.
List<string> result = new List<string>();
// 2.
// Store a stack of our directories.
Stack<string> stack = new Stack<string>();
// 3.
// Add initial directory.
stack.Push(b);
// 4.
// Continue while there are directories to process
while (stack.Count > 0)
{
// A.
// Get top directory
string dir = stack.Pop();
try
{
// B
// Add all files at this directory to the result List.
result.AddRange(Directory.GetFiles(dir, "*.*"));
// C
// Add all directories at this directory.
foreach (string dn in Directory.GetDirectories(dir))
{
stack.Push(dn);
}
}
catch
{
// D
// Could not open the directory
}
}
return result;
}
}
}
}
The code above works well for recursively finding what files/directories lie in a folder on my c:.
I am trying to serialize the results of what this code does to an XML file but I am not sure how to do this.
My project is this: find all files/ directories w/in a drive, serialize into an XML file. Then, the second time i run this app, i will have two XML files to compare. I then want to deserialize the XML file from the first time i ran this app and compare differences to the current XML file and produce a report of changes (i.e. files that have been added, deleted, updated).
I was hoping to get some help as I am a beginner in C# and i am very very shaky on serializing and deserializing. I'm having lots of trouble coding. Can someone help me?
Thanks
Your result is List<string> and that is not directly serializable. You'll have to wrap it, a minimal approach:
[Serializable]
class Filelist: List<string> { }
And then the (De)Serialization goes like:
Filelist data = new Filelist(); // replaces List<string>
// fill it
using (var stream = File.Create(#".\data.xml"))
{
var formatter = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
formatter.Serialize(stream, data);
}
data = null; // lose it
using (var stream = File.OpenRead(#".\data.xml"))
{
var formatter = new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
data = (Filelist) formatter.Deserialize(stream);
}
But note that you will not be comparing the XML in any way (not practical). You will compare (deserialzed) List instances. And the XML is SOAP formatted, take a look at it. It may not be very useful in another context.
And therefore you could easily use a different Formatter (binary is a bit more efficient and flexible).
Or maybe you just want to persist the List of files as XML. That is a different question.
For anyone who is having trouble with xml serialization and de-serialization. I have created a sample class to do this below. It works for recursive collections also (like files and directories).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Sample
{
[Serializable()]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false, ElementName = "rootnode")]
public partial class RootNode
{
[System.Xml.Serialization.XmlElementAttribute("collection1")]
public List<OuterCollection> OuterCollections { get; set; }
}
[Serializable()]
public partial class OuterCollection
{
[XmlAttribute("attribute1")]
public string attribute1 { get; set; }
[XmlArray(ElementName = "innercollection1")]
[XmlArrayItem("text", Type = typeof(InnerCollection1))]
public List<InnerCollection1> innerCollection1Stuff { get; set; }
[XmlArray("innercollection2")]
[XmlArrayItem("text", typeof(InnerCollection2))]
public List<InnerCollection2> innerConnection2Stuff { get; set; }
}
[Serializable()]
public partial class InnerCollection2
{
[XmlText()]
public string text { get; set; }
}
public partial class InnerCollection1
{
[XmlText()]
public int number { get; set; }
}
}
This class serializes and deserializes itself....hopefully this helps.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
namespace TestStuff
{
public class Configuration
{
#region properties
public List<string> UIComponents { get; set; }
public List<string> Settings { get; set; }
#endregion
//serialize itself
public string Serialize()
{
MemoryStream memoryStream = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(Configuration));
using (StreamWriter xmlTextWriter = new StreamWriter(memoryStream))
{
xs.Serialize(xmlTextWriter, this);
xmlTextWriter.Flush();
//xmlTextWriter.Close();
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
memoryStream.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(memoryStream);
return reader.ReadToEnd();
}
}
//deserialize into itself
public void Deserialize(string xmlString)
{
String XmlizedString = null;
using (MemoryStream memoryStream = new MemoryStream())
{
using (StreamWriter w = new StreamWriter(memoryStream))
{
w.Write(xmlString);
w.Flush();
XmlSerializer xs = new XmlSerializer(typeof(Configuration));
memoryStream.Seek(0, SeekOrigin.Begin);
XmlReader reader = XmlReader.Create(memoryStream);
Configuration currentConfig = (Configuration)xs.Deserialize(reader);
this.Settings = currentConfig.Settings;
this.UIComponents = currentConfig.UIComponents;
w.Close();
}
}
}
static void Main(string[] args)
{
Configuration thisConfig = new Configuration();
thisConfig.Settings = new List<string>(){
"config1", "config2"
};
thisConfig.UIComponents = new List<string>(){
"comp1", "comp2"
};
//serializing the object
string serializedString = thisConfig.Serialize();
Configuration myConfig = new Configuration();
//deserialize into myConfig object
myConfig.Deserialize(serializedString);
}
}
}
John:
May I suggest an improvement? Instead of using filenames, use the FileInfo object. This will allow you to get much more accurate information about each file rather than just if it exists under the same name.
Also, the XmlSerializer class should do you just fine. It won't serialize generic lists, so you'll have to output your List<> to an array or some such, but other than that:
XmlSerializer serial = new XmlSerializer(typeof(FileInfo[]));
StringWriter writer = new StringWriter();
FileInfo[] fileInfoArray = GetFileInfos();
serial.Serialize(writer, fileInfoArrays);
Simple and easy, unless it matters to you how the serialized XML looks.
Whatever you do, lose the empty catch block. You WILL regret swallowing exceptions. Log them or re-throw them.
Help #1. Indent code by four spaces to have it be seen as code when you post here.
2: get rid of that try/catch block, as it will eat all exceptions, including the ones you want to know about.
3: Did you make any attempt at all to serialize your results? Please edit your question to show what you tried. Hint: use the XmlSerializer class.
for xml serialisation, you have several possibilities:
Doing it manually
Using XmlSerializer as detailed above
Using System.Xml.Serialization
Using Linq to Xml
for the last two, see a code example in this answer. (See some gotchas here)
And for your recursive directory visitor, you could consider making it really recursive: here's some interesting code examples.
This question is exactly like this one. I also have a posted answer which will work for you as well:
How to serialize?

Categories