I have a GetServiceMap() method which calls deserializer who then opens the stream and reads something from it.
The problem is that i have a GetAllGroups() method also who calls deserializer over the same stream.
How would i syncronize it? With ManualResetEvent maybe?
public ServiceMapModel GetServiceMap()
{
s._mre.WaitOne();
return s.Deserialize();
}
public List<Group> GetAllGroups()
{
s._mre.WaitOne();
return s.Deserialize().Groups;
}
Deserialize method:
public ManualResetEvent _mre = new ManualResetEvent(true);
public ServiceMapModel Deserialize()
{
_serviceMap = new ServiceMapModel();
_mre.Reset();
try
{
using (var fileStream = new FileStream(Settings.Path, FileMode.Open))
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
using (XmlReader reader = XmlReader.Create(fileStream, settings))
{
_serviceMap = _serializer.Deserialize(reader) as ServiceMapModel;
}
fileStream.Close();
}
}
catch (IOException)
{
}
_mre.Set();
return _serviceMap;
}
For your case basic lock should be enough - no reason to use more complicated objects.
I would actually cache result of deserialization instead of reading from file every time, but it is your call.
Related
In C# Dot Net, How to handle a exception when you want to de-serialize a xml file, but by default the file doesn't exists! because you have to run the program to create one.
Below is the area where I need Help.
public static Compare_Data[] Deserialize()
{
Compare_Data[] cm;
cm = null;
string path = #"C:\Users\XYZ\Desktop\BACKUP_DATA\log.xml";
XmlSerializer xs = new XmlSerializer(typeof(Compare_Data[]));
if (File.Exists(path))
{
using (FileStream fs = new FileStream(path, FileMode.Open))
{
// This will read the XML from the file and create the new instance of Compare_Data.
cm = (Compare_Data[])xs.Deserialize(fs);
return cm;
}
}
else
{
using (FileStream fs = new FileStream(path, FileMode.Create))
{
xs.Serialize(fs); /// what to add here ?
}
}
return null;
}
If general, you don't want your methods to have side effects. In this case, creating an empty log file in the else branch is probably unnecessary and should be handled by a separate Serialize() method when there is data to be logged.
Your code could be simplified something like this:
public static Compare_Data[] Deserialize()
{
const string path = #"C:\Users\XYZ\Desktop\BACKUP_DATA\log.xml";
if (!File.Exists(path))
{
// return null or an empty array, depending on how
// you want the calling code to handle this.
return null;
}
using (FileStream fs = new FileStream(path, FileMode.Open))
{
var xs = new XmlSerializer(typeof(Compare_Data[]));
return (Compare_Data[])xs.Deserialize(fs);
}
}
I have a method that returns object from .xml file
(please don't mind resource usage and naming, it's just an example)
public static T FromXMLFile<T>(string filePath)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
FileStream fs = new FileStream(filePath, FileMode.Open);
XmlTextReader xmlTextReader = new XmlTextReader(fs);
if(xmlSerializer.CanDeserialize(xmlTextReader))
{
object tempObject = (T)xmlSerializer.Deserialize(xmlTextReader );
xmlTextReader.Close();
return (T)tempObject;
}
else
return default(T);
}
Now I would like to do the same but with with string instead of a file. I came up with something like this (again, simplified example)
public static T FromString<T>(string inputString)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
T result;
try
{
using (TextReader reader = new StringReader(inputString))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
catch //temporary solution, finally should stick to .CanDeserialize(xmlTextReader) usage
{
return default(T);
}
}
How would I use .CanDeserialize() in this case?
Rather than using the Deserialize(TextReader) overload, create an XmlReader from the TextReader, and use that XmlReader for both the Deserialize and CanDeserialize calls:
using (TextReader reader = new StringReader(inputString))
using (XmlReader xmlReader = XmlReader.Create(reader))
{
if (serializer.CanDeserialize(xmlReader))
{
result = (T)serializer.Deserialize(xmlReader);
}
}
This approach - with both read and write - also allows you to supply additional reader/writer settings for fine-grained control of the API.
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.
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.
I am facing the problem regarding the following issue in wp7
"Type 'System.Windows.Media.Transform' cannot be serialized in C#"
When i call the below method to save my List data to isolated storage
SerializeHelper.SaveSetting("myfile.Xml",swaplist);
then then i am getting the exception.
public static class SerializeHelper
{
public static void SaveSetting<T>(string fileName, T dataToSave)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
try
{
using (var stream = store.CreateFile(fileName))
{
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(stream, dataToSave);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message);
return;
}
}
}
}
I am attaching the screenshot of structure of list data
How to resolve this?
Thank you for adding the screenshot... and pasting some code. Can't really see anything wrong.
does you VM only expose public primitive / serialisable types ? I have in past used something like this to serialise to iso store.
public static void SaveObjectToStorage<T>(T ObjectToSave)
{
TextWriter writer;
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream fs = isf.OpenFile(GetFileName<T>(), System.IO.FileMode.Create))
{
writer = new StreamWriter(fs);
XmlSerializer ser = new XmlSerializer(typeof(T));
ser.Serialize(writer, ObjectToSave);
writer.Close();
}
}
}