Hi I am wondering how I can keep the previous records with my stream writer, if I use the below code it works fine when the student record is created but when I create a second student record the previous record is gone? How can I keep all records?
public void AddStudent(Student student)
{
students.Add(student);
XmlSerializer s = new XmlSerializer(typeof(Student));
TextWriter w = new StreamWriter("c:\\list.xml");
s.Serialize(w, student);
w.Close();
}
EDIT UPDATE:
From the partial answer below I keep getting this error Type WcfServiceLibrary1.Student' in Assembly 'WcfServiceLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null is not marked as serializable
I have decorated the Student Class with [Serializable()] so im not sure whats going on?
Use this overload of StreamWriter's constructor to append new data instead of overwriting.
TextWriter w = new StreamWriter("c:\\list.xml", true);
Update:
I see, it works only with BinaryFormatter and not with XmlSerializer, because second write makes XML invalid. Unless you need XML format for some reason, using binary format is easier. This should work:
static void WriteStudent(Student S)
{
BinaryFormatter f = new BinaryFormatter();
using (Stream w = new FileStream("c:\\list.dat", FileMode.Append))
{
f.Serialize(w, S);
}
}
static List<Student> ReadStudents()
{
BinaryFormatter f = new BinaryFormatter();
using (Stream w = new FileStream("c:\\list.dat", FileMode.Open))
{
List<Student> students = new List<Student>();
while (w.Position < w.Length)
{
students.Add((Student)f.Deserialize(w));
}
return students;
}
}
What you are doing is opening a file, discard the existing content, serialize a single student object of type Student into XML, write the XML into a file and close it.
What you have to do is almost exactly the same, except that you have to serialize a list of students rather than a single one. For that purpose, try doing this:
s.Serialize(w, students);
instead of this:
s.Serialize(w, student);
And don't forget to change typeof(Student) to the typeof of whatever class you are using to maintain a list (that will be a type of students object).
It won't work the way you expecting to. StreamWriter will not do any magic to insert your new Student record inside the root element. You need to deserialize the list from file, add new entry and serialize it back. Alternatively you can keep the list of students in memory, add new record to it and then serialize the entire set.
Neither of those are particularly great ways to store incremental updates. You should look into using SQL Server (or express) instead.
Related
Normally I use SQL Server or Oracle for storage, so serializing to XML is a little new for me. I've had trouble locating answers that mirror what I'm doing enough for me to grasp what I've done wrong.
I have object MyObject and it has multiple properties. it is serializable. I need to store an IEnumerable<MyObject> to an xml file. This file is overwritten by a new list when saving, and when read needs to read directly back to an IEnumerable<MyObject> again.
I've managed to suppress the XML declaration and everything just fine on writes past the first, but I'm stuck on how to store this in a way I can read it back. Existing code (partially from searching around on here):
foreach (var i in items)
{
bool append = File.Exists(fileName);
using (var file = new StreamWriter(fileName,append))
{
///don't add XML declarative headers if the file already exists.
if (append == true)
{
///check to see if the
var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
using (var writer = XmlWriter.Create(file, settings))
{
xml.Serialize(writer, i, emptyNamespaces);
}
}
else
{
xml.Serialize(file, i);
//append = true;
}
}
}
Obviously, I'm looping through the list, with only the first item having the XML header information. The problem is, I get multiple root nodes this way. if I create a new root node manually, I can't seem to serialize back to MyObject> because it doesn't match a property or class. What approach should I use to be able to serialize and deserialize this list of objects?
I need to deserialize Dictionary<> in C#. Since, XmlSerializer cannot be used for generic Dictionary type, I thought of using DataContractSerializer.I had a StreamReader in which the data was to be deserialized, but it showed unmatching overload parameters on using with DataContractSerializer.ReadObject.
My previous code was:
StreamReader isr = new StreamReader(File.OpenRead(algorithmName + ".conf"));
configuration.Load(isr);
The Load method is defined as:
public void Load(StreamReader isr)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(the class containing this method));
XmlDictionaryReader read = XmlDictionaryReader.CreateTextReader(isr, new XmlDictionaryReaderQuotas());
dcs.ReadObject(isr);
}
The Load method is contained in class that implements Dictionary<>
But this showed parameter mismatched error due to StreamReader as parameter for ReadObject().
So, I modified the code as:
FileStream isr = File.OpenRead(algorithmName + ".conf");
configuration.Load(isr);
And the Load method as:
public void Load(FileStream isr)
{
DataContractSerializer dcs = new DataContractSerializer(typeof(the class containing Load method));
XmlDictionaryReader read = XmlDictionaryReader.CreateTextReader(isr, new XmlDictionaryReaderQuotas());
dcs.ReadObject(isr);
}
Now there are no errors but I wanted to know whether using DataContractSerializer in place of XmlSerializer is correct or not. Also, any updates on replacing streamreader with filestream would be highly appreciated.
I am trying to serialize my Report class info to an XML. At this point I think all of the serialize and deserialize code works, but for the initial write, I'm having trouble performing the serialize, because the XML file doesn't exist yet.
for an empty text file, i can use:
StreamWriter sw = File.CreateText(#"path");
sw.Close();
this is my code block for the serializing. the exception (Directory not found) is getting thrown on the StreamWriter line. I'd like to simply add an if(!File.Exists(xmlPath))...create empty XML. Or maybe there is a more correct way to do this.
public void SerializeToXML(Report newReport)
{
XmlSerializer serializer = new XmlSerializer(typeof(Report));
TextWriter textWriter = new StreamWriter(xmlPath);
serializer.Serialize(textWriter, newReport);
textWriter.Close();
}
The StreamWriter(String) constructor will create the file if it does not already exist:
If the file exists, it is overwritten; otherwise, a new file is created.
However, it will not create any inexistent directories in your path.
DirectoryNotFoundException: The specified path is invalid, such as being on an unmapped drive.
To create any required directories, you can include the following code (at the beginning of your SerializeToXML method):
var dir = Path.GetDirectoryName(xmlPath);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
First to make sure the directory exist you can use:
Directory.CreateDirectory(#"c:\directory\subdirectory");
You don't have to check if directory already exist.
A easy way to convert public classes to XML is to use the following snippet:
public static string ToXml<T>(T obj)
{
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms))
{
var xmlSer = new XmlSerializer(typeof(T));
xmlSer.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
return sr.ReadToEnd();
}
}
Then you could just use the following code to write it to a file:
var xmlString = Util.ToXml(report);
File.WriteAllText(#"path", xmlString);
(this example is without error handling)
Also, in your code you forgot to close/dispose the TextWriter. I would recommend using the using-statement to handle it for you.
CreateText, and the StreamWriter, will create files if they don't exist, but they won't create directories that don't already exist for you. Is your path correct?
Try Checking with a Directory.Exists(Path.GetDirectoryName(xmlPath)).
I am generating an xml file. I noticed in the file it has a tag I do not want. I am generating the xml file from the xmlSerializer object and what its doing is handling a Property on my object wrong...The proerty on my object lloks like this...
public List<ProductVarient> Varients { get; set; }
So when I serialize it, I get a structure like this
<Varients>
<ProductVarient>
<Name>Nick</Name>
......
I want just
<AnotherProp>
<Stuff>toys</Stuff>
</AnotherProp>
<ProductVarient>
<Name>Nick</Name>
</ProductVarient>
<ProductVarient>
<Name>Jeff</Name>
</ProductVarient>
....
So instead of trying to workaround the xmlserializer, I went with a super hack and wrote this code
string s = File.ReadAllText(path);
s.Replace("<Varients>", "");
s.Replace("</Varients>", "");
using (FileStream stream = new FileStream(path, FileMode.Create))
using (TextWriter writer = new StreamWriter(stream))
{
writer.WriteLine("");
writer.WriteLine(s);
}
2 Questions
-The code I wrote wont Replace with "", it doesnt throw an exception but it doesnt work either, im not sure why?
-Is there a quick better way to accomplish my problem.
Try:
s = s.Replace("<Varients>", "");
s = s.Replace("</Varients>", "");
String is immutable, and methods like Replace return their result instead of mutating the receiver.
UPDATE: But, a better solution, as stated by John Saunders, is to use the XmlSerializer to achieve what you want:
[XmlElement("ProductVarient")]
public List<ProductVarient> Varients { get; set; }
Instead of trying to "work around" the XmlSerializer, you should learn to use it properly.
Try placing [XmlElement] on your property:
[XmlElement]
public List<ProductVarient> Varients { get; set; }
Alternatively, you can try [XmlArray] and [XmlArrayItem] attributes. You haven't shown a good example of the XML you want (what do you want if there are multiple items in the list?), so I can't tell you which you should use.
Bad hack and should be sent to it's room, but to answer your question:
string s = File.ReadAllText(path);
s = s.Replace("<Varients>", "");
s = s.Replace("</Varients>", "");
using (FileStream stream = new FileStream(path, FileMode.Create))
using (TextWriter writer = new StreamWriter(stream))
{
writer.WriteLine("");
writer.WriteLine(s);
}
Replace returns a modified string, just using the extension doesn't do anything unless the result is returned.
I pretty sure I have to create some sort of mold of what the XML file has to look like first, right?
Any help will be appreciated.
One simple way to do this would be to create .NET classes that you put the data in and then use XmlSerializer to serialize the data to a file and then later deserialize back into an instance of the class and re-populate the form.
AS an example, if you have a form with customer data. To keep it short we will just have first name and last name. You can create a class to hold the data. Keep in mind this is just a simple example, you can store arrays and all kinds of complex/nested data like this.
public class CustomerData
{
public string FirstName;
public string LastName;
}
So save the data as XML your code will look something like this.
// Create an instance of the CustomerData class and populate
// it with the data from the form.
CustomerData customer = new CustomerData();
customer.FirstName = txtFirstName.Text;
customer.LastName = txtLastName.Text;
// Create and XmlSerializer to serialize the data to a file
XmlSerializer xs = new XmlSerializer(typeof(CustomerData));
using (FileStream fs = new FileStream("Data.xml", FileMode.Create))
{
xs.Serialize(fs, customer);
}
And loading the data back would be something like the following
CustomerData customer;
XmlSerializer xs = new XmlSerializer(typeof(CustomerData));
using (FileStream fs = new FileStream("Data.xml", FileMode.Open))
{
// This will read the XML from the file and create the new instance
// of CustomerData
customer = xs.Deserialize(fs) as CustomerData;
}
// If the customer data was successfully deserialized we can transfer
// the data from the instance to the form.
if (customer != null)
{
txtFirstName.Text = customer.FirstName;
txtLastName.Text = customer.LastName;
}
look at using Linq to xml - http://msdn.microsoft.com/en-us/library/bb387098.aspx
there are tutorials here that will guide you through creating and querying an xml document.
So, are you wanting to collect data from a user in a Windows form application and then write it out to an XML file (when they click OK)? If so, I'd check out the XmlTextWriter class ( http://msdn.microsoft.com/en-us/library/system.xml.xmltextwriter.aspx ).