Serializing an object using Json.Net causes Out of Memory exception - c#

Disclaimer: I did went through most of the solution provided here but most of them were talking about OOM exception while Deserialization.
I am trying to serialize an object( it's a Tree) into Json using Json.Net. Everything works fine for small objects but i get OOM exception when i try it with large objects. As it works with smaller object of same datatype i am assuming there is no circular reference (I did inspect my data structure for it). Is there a way where i can convert my object into stream ( this is a Windows Store app ) and generate the Json using that stream ?
public static async Task<bool> SerializeIntoJson<T>(string fileName, StorageFolder destinationFolder, Content content)
{
ITraceWriter traceWriter = new MemoryTraceWriter();
try
{
string jsonString = JsonConvert.SerializeObject(content, Formatting.Indented, new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
TypeNameHandling = TypeNameHandling.All,
Error = ReportJsonErrors,
TraceWriter = traceWriter,
StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
});
System.Diagnostics.Debug.WriteLine(traceWriter);
StorageFile file = await destinationFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await Windows.Storage.FileIO.WriteTextAsync(file, jsonString);
return true;
}
catch (NullReferenceException nullException)
{
System.Diagnostics.Debug.WriteLine(traceWriter);
logger.LogError("Exception happened while serializing input object, Error: " + nullException.Message);
return false;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(traceWriter);
logger.LogError("Exception happened while serializing input object, Error: " + e.Message, e.ToString());
return false;
}
}
In order to convert my object into stream, the code i found out was using a BinaryFormatter which is not available in Windows store app dll's.

It is due to the large amount of records you are trying to serialize, which occupies a large amount of memory. Solutions which I have found for this error is to directly write to the documents using StreamWriter(JsonWriter or TextWriter).
If you have Object use TextWriter:
using (TextWriter textWriter = File.CreateText("LocalJsonFile.json"))
{
var serializer = new JsonSerializer();
serializer.Serialize(textWriter, yourObject);
}
If you have a string, use StringWriter:
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
using (JsonWriter textWriter = new JsonTextWriter(sw))
{
var serializer = new JsonSerializer();
serializer.Serialize(textWriter, yourObject);
}

Updated Code based on suggestions in the comments on the question, This works!
public static async Task<bool> SerializeIntoJson<T>(string fileName, StorageFolder destinationFolder, Content content)
{
try
{
StorageFile file = await destinationFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
using (var stream = await file.OpenStreamForWriteAsync())
{
StreamWriter writer = new StreamWriter(stream);
JsonTextWriter jsonWriter = new JsonTextWriter(writer);
JsonSerializer ser = new JsonSerializer();
ser.Formatting = Newtonsoft.Json.Formatting.Indented;
ser.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
ser.TypeNameHandling = TypeNameHandling.All;
ser.Error += ReportJsonErrors;
ser.Serialize(jsonWriter, content);
jsonWriter.Flush();
}
return true;
}
catch (NullReferenceException nullException)
{
logger.LogError("Exception happened while serializing input object, Error: " + nullException.Message);
return false;
}
catch (Exception e)
{
logger.LogError("Exception happened while serializing input object, Error: " + e.Message, e.ToString());
return false;
}
}

Related

XMLSerializer - issue with UTF-8 vs UTF-16 Code

I am trying to serialize a simple object (5 string properties) into XML to save to a DB Image field. Then I need to DeSerialize it back into a string later in the program.
However, I am getting some errors - caused by the XML being saved thinking it is in UTF-16 - however, when I load it from the DB back into a string - it thinks it is a UTF 8 String.
The error I get is
InnerException {"There is no Unicode byte order mark. Cannot switch to Unicode."} System.Exception {System.Xml.XmlException}
-- Message "There is an error in XML document (0, 0)." string
Is this happening because of the two different ways I save and load the string to/from the DB? On the save I am using a StringBuilder - but on the load from DB I am using just a String.
Thoughts?
Serialize and Save to DB
// Now Save the OBject XML to the Query Tables
var serializer = new XmlSerializer(ExportConfig.GetType());
StringBuilder StringResult = new StringBuilder();
using (var writer = XmlWriter.Create(StringResult))
{
serializer.Serialize(writer, ExportConfig);
}
//MessageBox.Show("XML : " + StringResult);
// Now Save to the Query
try
{
string UpdateSQL = "Update ZQryRpt "
+ " Set ExportConfig = " + TAGlobal.QuotedStr(StringResult.ToString())
+ " where QryId = " + TAGlobal.QuotedStr(((DataRowView)bindingSource_zQryRpt.Current).Row["QryID"].ToString())
;
ExecNonSelectSQL(UpdateSQL, uniConnection_Config);
}
catch (Exception Error)
{
MessageBox.Show("Error Setting ExportConfig: " + Error.Message);
}
Load from DB And Deserialize
byte[] binaryData = (byte[])((DataRowView)bindingSource_zQryRpt.Current).Row["ExportConfig"];
string XMLStored = System.Text.Encoding.UTF8.GetString(binaryData, 0, binaryData.Length);
if (XMLStored.Length > 0)
{
IIDExportObject ExportConfig = new IIDExportObject();
var serializer = new XmlSerializer(ExportConfig.GetType());
//StringBuilder StringResult = new StringBuilder(XMLStored);
// Load the XML from the Query into the StringBuilder
// Now we need to build a Stream from the String to use in the XMLReader
byte[] byteArray = Encoding.UTF8.GetBytes(XMLStored);
MemoryStream stream = new MemoryStream(byteArray);
using (var reader = XmlReader.Create(stream))
{
ExportConfig = (IIDExportObject)serializer.Deserialize(reader);
}
}
John - thank you very much for the comment! It allowed me to complete the code and find a solution.
As you noted - using a stream reader was the solution - but I could not read the first line because there was only one 'line' in my string. However, I could use the line
using (StreamReader sr = new StreamReader(stream, false))
Which allows me to read the stream and ignore the "Byte Order Mark Detection" set to false.
string XMLStored = MainFormRef.GetExportConfigForCurrentQuery();
if (XMLStored.Length > 0)
{
IIDExportObject ExportConfig = new IIDExportObject();
try
{
var serializer = new XmlSerializer(ExportConfig.GetType());
// Now we need to build a Stream from the String to use in the XMLReader
byte[] byteArray = Encoding.UTF8.GetBytes(XMLStored);
MemoryStream stream = new MemoryStream(byteArray);
// Now we need to use a StreamReader to get around UTF8 vs UTF16 issues
// A little cumbersome - but it works
using (StreamReader sr = new StreamReader(stream, false))
{
using (var reader = XmlReader.Create(sr))
{
ExportConfig = (IIDExportObject)serializer.Deserialize(reader);
}
}
}
catch
{
}
I am not sure this is the best solution - but it works. I will be curious to see if anyone else has a better way of dealing with this.
Thanks to G Bradley, I took his answer and generalized it a bit to make it a bit easier to call.
public static string SerializeToXmlString<T>(T objectToSerialize)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = false;
settings.Encoding = Encoding.UTF8;
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
serializer.Serialize(writer, objectToSerialize);
}
return builder.ToString();
}
public static T DeserializeFromXmlString<T>(string xmlString)
{
if (string.IsNullOrWhiteSpace(xmlString))
return default;
var serializer = new XmlSerializer(typeof(T));
byte[] byteArray = Encoding.UTF8.GetBytes(xmlString);
MemoryStream stream = new MemoryStream(byteArray);
using (StreamReader sr = new StreamReader(stream, false))
{
using (var reader = XmlReader.Create(sr))
{
return (T)serializer.Deserialize(reader);
}
}
}

Saving XDocument to file in PCL C#

I'm working within my PCL library and need to serialise a class and output to a file. I'm very short on space, so don't have the space for PCLStorage.
Currently I'm using this for the serialisation. IFilePath returns a file path from the non-PCL part.
IFilePath FilePath;
public void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
try
{
using (var ms = new MemoryStream())
{
var xmlDocument = new XDocument();
using (var writer = xmlDocument.CreateWriter())
{
var serialize = new DataContractSerializer(typeof(T));
serialize.WriteObject(writer, serializableObject);
xmlDocument.Save(ms, SaveOptions.None);
}
}
}
catch (Exception ex)
{
//Log exception here
}
}
When I try to save, nothing is showing. I have a feeling it's because I'm not outputting the stream to a file, but I'm at a loss as how to do this.
You are trying to save to a file, an action which is specific for each platform.
PCLStorage is implementing this functionality for each platform and this is what you will have to do also if you can"t use it.
In you case what you have to do is to create the stream (in each platform) in your non pcl code and then pass it to your function which will look like this:
public void SerializeObject<T>(T serializableObject, Stream fileStream)
{
if (serializableObject == null) { return; }
try
{
var xmlDocument = new XDocument();
using (var writer = xmlDocument.CreateWriter())
{
var serialize = new DataContractSerializer(typeof(T));
serialize.WriteObject(writer, serializableObject);
xmlDocument.Save(fileStream, SaveOptions.None);
}
}
catch (Exception ex)
{
//Log exception here
}
}
more on pcl here.
Problem is that your variable ms in using (var ms = new MemoryStream()) is empty and does not point to any file location of which MemoryStream does not receive a filepath as argument. I propose you use a StreamWriter instead and pass the your FileStream to it. Example
Use your fileName to create a FileStream which inherits from the Stream class then replace the Memory stream with the newly created filestream like this.
using(FileStream stream = File.OpenWrite(fileName))
{
var xmlDocument = new XDocument();
using (var writer = xmlDocument.CreateWriter())
{
var serialize = new DataContractSerializer(typeof(T));
serialize.WriteObject(writer, serializableObject);
xmlDocument.Save(stream, SaveOptions.None);
}
}
Hope this helps.

C# Avro Schema not encoded with data stream

I'm using Avro to serialize objects and then add them to Kafka messages that will be consumed and deserialized by clients. I've tried several different approaches for serialization but none of them seem to embed the schema in the data stream. Here is the latest version of my serialization code. You can see the commented out attempts to use the various writers available.
public static byte[] Serialize<T>(T recordObj) where T : ISpecificRecord
{
Log.Info("Serializing {0} object to Avro.", typeof(T));
try
{
using (var ms = new MemoryStream())
{
var encoder = new BinaryEncoder(ms);
//var writer = new SpecificDefaultWriter(recordObj.Schema);
var writer = new SpecificDatumWriter<T>(recordObj.Schema);
//writer.Write(recordObj.Schema, recordObj, encoder);
writer.Write(recordObj, encoder);
return ms.ToArray();
}
}
catch (Exception ex)
{
Log.Error("Failed to Avro serialize object. {0}", ex);
return null;
}
}
I'm not really sure what else to try.
After digging around in the actual Avro code, I found out I needed a FileWriter, but could not figure out how to instantiate one as DataFileWriter has no public constructor. Turns out there is a static method on the DataFileWriter class called OpenWriter which takes in a DatumWriter and a Stream and returns a DataFileWriter. The code below now properly includes object metadata in the result data stream.
public static byte[] Serialize<T>(T recordObj) where T : ISpecificRecord
{
Log.Info("Serializing {0} object to Avro.",typeof(T));
try
{
using(var ms = new MemoryStream())
{
var specDatumWriter = new SpecificDatumWriter<T>(recordObj.Schema);
var specDataWriter = Avro.File.DataFileWriter<T>.OpenWriter(specDatumWriter, ms);
specDataWriter.Append(recordObj);
specDataWriter.Flush();
specDataWriter.Close();
return ms.ToArray();
}
}
catch(Exception ex)
{
Log.Error("Failed to Avro serialize object. {0}",ex);
return null;
}
}

Serializing to MemoryStream causes an OutOfmemoryException, but serializing to a FileStream does not. Can anyone tell me why?

I'm using Newtonsoft Json.Net to serialize objects as json. I continually run into an OutOfMemoryException when I try to serialize to a MemoryStream, but not when I serialize to a FileStream. Could someone explain why this might be happening? These are the two methods that I am using to serialize.
Throws an OutOfMemoryException
private static MemoryStream _serializeJson<T>(T obj)
{
try
{
var stream = new MemoryStream();
var streamWriter = new StreamWriter(stream);
var jsonWriter = new JsonTextWriter(streamWriter);
var serializer = new JsonSerializer();
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
serializer.Formatting = Formatting.Indented;
serializer.Serialize(jsonWriter, obj);
streamWriter.Flush();
stream.Position = 0;
return stream;
}
catch (Exception e)
{
//Logger.WriteError(e.ToString());
Console.WriteLine(e.ToString());
return null;
}
}
Doesn't throw an OutOfMemoryException
private static void _serializeJsonToFile<T>(T obj, string path)
{
try
{
using (FileStream fs = File.Open(path, FileMode.Create, FileAccess.ReadWrite))
using (StreamWriter sw = new StreamWriter(fs))
using (JsonWriter jw = new JsonTextWriter(sw))
{
jw.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer();
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
serializer.Serialize(jw, obj);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
P.S. some might ask why I would want to return a stream instead of simply serializing to the file stream. This is because I want to keep serialization in one class and file handling in another, so I'm passing the memory stream to a WriteFile method in another class later.
You are getting OutOfMemoryExceptions because the memory stream is very aggressive about it's growth. Every time it needs to re-size it doubles it's internal buffer.
//The code from MemoryStream http://referencesource.microsoft.com/mscorlib/system/io/memorystream.cs.html#1416df83d2368912
private bool EnsureCapacity(int value) {
// Check for overflow
if (value < 0)
throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
if (value > _capacity) {
int newCapacity = value;
if (newCapacity < 256)
newCapacity = 256;
// We are ok with this overflowing since the next statement will deal
// with the cases where _capacity*2 overflows.
if (newCapacity < _capacity * 2)
newCapacity = _capacity * 2;
// We want to expand the array up to Array.MaxArrayLengthOneDimensional
// And we want to give the user the value that they asked for
if ((uint)(_capacity * 2) > Array.MaxByteArrayLength)
newCapacity = value > Array.MaxByteArrayLength ? value : Array.MaxByteArrayLength;
Capacity = newCapacity;
return true;
}
return false;
}
With a 17.8 MB file that is a worst case scenario of a 35.6 MB byte array being used. The old byte arrays that where discarded during the resizing process can also cause Memory Fragmentation depending on how long they live, this can easily get your program to throw a OOM error before you get to the 32 bit memory limit.
Writing directly to a FileStream does not require any large buffers to be created in memory so it uses much less space.
There is a way to separate the logic of the saving from the serializing, just pass in the stream to the function instead of creating it in the function itself.
private static void _serializeJson<T>(T obj, Stream stream)
{
try
{
using(var streamWriter = new StreamWriter(stream, Encoding.UTF8, 1024, true))
using(var jsonWriter = new JsonTextWriter(streamWriter))
{
var serializer = new JsonSerializer();
serializer.ContractResolver = new CamelCasePropertyNamesContractResolver();
serializer.Formatting = Formatting.Indented;
serializer.Serialize(jsonWriter, obj);
}
}
catch (Exception e)
{
//Logger.WriteError(e.ToString());
Console.WriteLine(e.ToString());
}
}
I also dispose of the StreamWriter that is created, the constructor I used has a leaveOpen flag which causes the underlying stream to not be closed when you dispose of the StreamWriter.

How to Deserialize using binary Deserialization from file [duplicate]

This question already has an answer here:
How to Deserialize using binary Deserialization from file text file
(1 answer)
Closed 8 years ago.
public static void SaveRestaurantList(List<Restaurant> restaurantList)
{
FileStream fs = new FileStream("Restaurant.txt", FileMode.Create, FileAccess.Write);
BinaryFormatter bf = new BinaryFormatter();
for (int i = 0; i < restaurantList.Count; i++)
{
Restaurant r = new Restaurant();
r = (Restaurant)restaurantList[i];
bf.Serialize(fs, r);
fs.Flush();
}
fs.Close();
Console.WriteLine("\n\n\t\t File Get Serialized.., \n\t\t Close the Promt and Check in Application Debug Folder..!!");
}
I have Serailze the generic list which I have, into "Restaurant.txt" file.
now I want to Deserialize the same and return it into a Generic List, I have tried
but its not working and it is giving error "Invalid Cast Expression".
Can anyone please help in solving out this.
You should serialize the complete list itself.
using (Stream stream = File.Open("data.bin", FileMode.Create))
{
BinaryFormatter bin = new BinaryFormatter();
bin.Serialize(stream, restaurantList);
}
You can later deserialize the complete list like this
using (Stream stream = File.Open("data.bin", FileMode.Open))
{
BinaryFormatter bin = new BinaryFormatter();
var restaurantList=(List<Restaurant>)bin.Deserialize(stream);
}
Kapadni,
I am storing my list/BindingList of object in .xml file and may be below functions/code will help you to serialize and de-serialize object and store/retrieve from .xml file
BindingList<IntradayData> objIntradayDataList;
SerializeObject(objIntradayDataList, filepath);
objIntradayDataList = DeSerializeObject<BindingList<IntradayData>>(filepath);
public void SerializeObject<T>(T serializableObject, string fileName)
{
if (serializableObject == null) { return; }
try
{
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
}
}
catch (Exception ex)
{
//Log exception here
log.Error("SerializeObject ", ex);
}
}
public T DeSerializeObject<T>(string fileName)
{
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try
{
string attributeXml = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString))
{
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read))
{
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
}
catch (Exception ex)
{
//Log exception here
log.Error("DeSerializeObject ", ex);
}
return objectOut;
}

Categories