XML Serialize large collections of objects to xml - c#

What is the best way to serialize a large collection of objects?
I need to serialize 10 millions of collections from the database to xml.
I always have OutOfMemoryException.
Can you help me with it?

You need to do this in a Read loop one by one. Read one from DB and insert one to XML file by using XmlTextWriter.
Something like below should work for your case.
using (var xml = new XmlTextWriter ("filename", Encoding.UTF8))
{
xml.WriteStartElement ("container_name");
var cmd = new SqlCommand ("your query", dbConnection);
using (var reader = cmd.ExecuteReader ())
{
while (reader.Read ())
{
//
// Read from DB write to xml
//
}
}
xml.WriteEndElement (); // Close your container
}
For XmlTextWriter reference check MSDN

Related

How do I serialize and deserialize this IEnumerable<myObject> so that multiple instances are stored in one XML file?

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?

How to dispose XMLDocument object

I am trying to dispose the memory from an XmlDocument object
using (XmlNodeReader xnrAwards = new XmlNodeReader(ndListItems))
{
ndListItems.InnerXml = ndListItems.InnerXml.Replace("_x002e_", "_").Replace("ows_", "");
dsAward.ReadXml(xnrAwards, XmlReadMode.ReadSchema);
XmlDocument xdocAwards = new XmlDocument();
xdocAwards.LoadXml(ndListItems.OuterXml);
xdocAwards.Save(ABCListName + "_XML.xml");
}
Any idea on how to dispose the memory off this object as this is giving me an outofmemoryexception
Stop using XmlDocument if memory is a concern. It loads the entire document at once, which is causing you your issues.
Use instead a stream-based reader: XmlReader
This object chunks up the file into a buffer instead of loading the entire thing.
using (XmlReader reader = XmlReader.Create(file)) {
while (reader.Read()) {
//Do processing on each
}
}
Note this is a forward-only reader, and it's not as straight-forward to use as XmlDocument, but buffering your data will ensure you don't run into further memory exceptions.
If you're curious about the mechanism used for buffering, it's a yield return behind the scenes (which is actually compiled to a switch case if you want to get down to the nitty-gritty). Here is a blog post of someone doing something similar with a text file: http://jamesmccaffrey.wordpress.com/2011/02/04/processing-a-text-file-with-the-c-yield-return-pattern/
ref: http://msdn.microsoft.com/en-us/library/vstudio/system.xml.xmlreader

Record and replay human-readable protobuf messages using a file with protobuf-csharp-port

I'm using protobuf-csharp-port and I need the ability to record some protobuf messages for replay later. XML would be ideal for me, but I'm flexible as long as a human could go into the file, make changes, then replay the messages from the file.
Using this C# code:
MyRequest req =
MyRequest.CreateBuilder().SetStr("Lafayette").Build();
using (var stringWriter = new StringWriter())
{
var xmlWriterSettings = new XmlWriterSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
var xmlWriter = XmlWriter.Create(stringWriter, xmlWriterSettings);
ICodedOutputStream output = XmlFormatWriter.CreateInstance(xmlWriter);
req.WriteTo(output);
output.Flush();
string xml = stringWriter.ToString();
using (var streamWriter = new StreamWriter(#"Requests.txt"))
{
streamWriter.WriteLine(xml);
streamWriter.WriteLine(xml);
streamWriter.WriteLine(xml);
}
}
I produce the Requests.txt file containing:
<str>Lafayette</str>
<str>Lafayette</str>
<str>Lafayette</str>
However when I try to deserialize them back using:
var xmlReaderSettings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (var xmlReader = XmlReader.Create(#"Requests.txt", xmlReaderSettings))
{
ICodedInputStream input = XmlFormatReader.CreateInstance(xmlReader);
MyRequest reqFromFile;
while(!input.IsAtEnd)
{
reqFromFile =
ReverseRequest.CreateBuilder().MergeFrom(input).Build();
}
}
Only one MyRequest gets deserialized and the other two are ignored. (After reqFromFile is built, input.IsAtEnd == true.)
So back to my question: is there some way to read multiple human-readable protobuf messages from a file?
So back to my question: is there some way to read multiple human-readable protobuf messages from a file?
Well you're currently creating a single text file with multiple root elements - it's not a valid XML file.
The simplest approach would probably be to create a single XML document with all those requests in, then load it (e.g. with LINQ to XML) and create an XmlReader positioned at each of the root element's children. Each of those XmlReader objects should be able to then deserialize to a single protobuf message.
You could look at the Protocol buffer Editor
Alternatively there is Google own Text format which is a bit like JSon. You can convert between the 2 using the protoc command (look at encode / decode options, you also need the proto definition)

StreamWriter overwriting previous records?

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.

How can I save data from a Windows Form to an XML file?

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 ).

Categories