Isolated Storage - How to read the appended data - c#

I have a Game application(WP8), where we are saving the scores of multiple attampts and showing it to user.
I have a Object with fields noOfStonesPicked and noOfFruitsPicked.
Here is my code:
MyTopic topicObj = new MyTopic ();
for (int i = 0; i <= 2; i++)
{
Test mt = new Test();
mt.noOfStonesPicked = 12;
mt.noOfFruitsPicked= 20;
topicObj.Stats.Add(mt);
}
WritetestTopicState(topicObj);
Now 3 attempts with each one having noOfStonesPicked -12 and noOfFruitsPicked - 20
Now i have saving this like :
public static void WritetestTopicState(MyTopic topic)
{
try
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (StreamWriter sw = new StreamWriter(store.OpenFile("12.xml", FileMode.Append, FileAccess.Write)))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyTopic));
serializer.Serialize(sw, topic);
serializer = null;
}
}
}
catch (Exception)
{
throw;
}
}
Now how can i retrive these values and display ?
EDIT
This is what i have tried:
public static MyTopic ReadMockTestTopicState()
{
MyTopic topic = null;
try
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
// Read application settings.
if (isoStore.FileExists("11.xml"))
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (StreamReader SR = new StreamReader(store.OpenFile("12.xml", FileMode.Open, FileAccess.Read)))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyTopic));
topic = (MyTopic)serializer.Deserialize(SR);
serializer = null;
}
}
}
else
{
// If setting does not exists return default setting.
topic = new MyTopic();
}
}
}
catch (Exception)
{
throw;
}
return topic;
}

XmlSerializer serializer = new XmlSerializer(typeof(MyTopic));
StreamReader reader = new StreamReader(path);
_myTopic = (MyTopic)serializer.Deserialize(reader);
reader.Close();
This should be enough for deserializing, If your MyTopic object is properly serializable, I mean if properties of the MyTopic object are properly attributed for xml serialization.

Related

How to create different List inside one storing file

I have created one List like this (List-Lecture-) and I used to store data inside one file.so i created (Lecture.bin) file. my Question is -> if I need to insert another List like (List-salary-) and need to store inside the previous file that's means (Lecture.bin) is it possible to store? Or do I need to create another storing file like (salary.bin)??? I have given my code below .please give a solution. thank you
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace XYZ_System
{
public class fileAccessLectureCls
{
private string lectureFilename = "Lecture.bin";
public List<Lecturer> loadAllLecturer()
{
List<Lecturer> lst = null;
if (File.Exists(lectureFilename))
{
Stream stream = File.Open(lectureFilename, FileMode.Open);
BinaryFormatter bin = new BinaryFormatter();
lst = (List<Lecturer>)bin.Deserialize(stream);
stream.Close();
}
else
{
lst = new List<Lecturer>();
}
return lst;
}
public bool SetLectureDetails(Lecturer ObjLecture)
{
Stream stream = null;
bool flag = false;
try
{
List<Lecturer> Leclist = this.loadAllLecturer();
Leclist.Add(ObjLecture);
stream = File.Open(lectureFilename, FileMode.Create);
BinaryFormatter bnformatter = new BinaryFormatter();
bnformatter.Serialize(stream, Leclist);
flag = true;
}
catch
{
flag = false;
}
finally
{
stream.Close();
}
return flag;
}
public List<Lecturer> GetLectureDetails()
{
List<Lecturer> LecList = null;
Stream stream = null;
try
{
stream = File.Open(lectureFilename, FileMode.Open);
BinaryFormatter bnformatter = new BinaryFormatter();
LecList = (List<Lecturer>)bnformatter.Deserialize(stream);
loadAllLecturer();
}
catch
{
//throw ex;
}
finally
{
stream.Close();
}
return LecList;
}

Serializing a ConcurrentBag of XAML

I have, in my code, a ConcurrentBag<Point3DCollection>.
I'm trying to figure out how to serialize them. Of course I could iterate through or package it with a provider model class, but I wonder if it's already been done.
The Point3DCollections themselves are potentially quite large and could stand to be compressed to speed up reading and writing to and from the disk, but the response times I need for this are largely in the user interface scale. In other words, I prefer a binary formatting over a XAML-text formatting, for performance reasons. (There is a nice XAML-text serializer which is part of the Helix 3D CodeProject, but it's slower than I'd like.)
Is this a use case where I'm left rolling out my own serializer, or is there something out there that's already packaged for this kind of data?
Here are some extensions methods that handle string and binary serialization of Point3DCollection bags. As I said in my comment, I don't think there is a best way of doing this in all cases, so you might want to try both. Also note they're using Stream parameter as input so you can chain these with calls to GZipStream of DeflateStream.
public static class Point3DExtensions
{
public static void StringSerialize(this ConcurrentBag<Point3DCollection> bag, Stream stream)
{
if (bag == null)
throw new ArgumentNullException("bag");
if (stream == null)
throw new ArgumentNullException("stream");
StreamWriter writer = new StreamWriter(stream);
Point3DCollectionConverter converter = new Point3DCollectionConverter();
foreach (Point3DCollection coll in bag)
{
// we need to use the english locale as the converter needs that for parsing...
string line = (string)converter.ConvertTo(null, CultureInfo.GetCultureInfo("en-US"), coll, typeof(string));
writer.WriteLine(line);
}
writer.Flush();
}
public static void StringDeserialize(this ConcurrentBag<Point3DCollection> bag, Stream stream)
{
if (bag == null)
throw new ArgumentNullException("bag");
if (stream == null)
throw new ArgumentNullException("stream");
StreamReader reader = new StreamReader(stream);
Point3DCollectionConverter converter = new Point3DCollectionConverter();
do
{
string line = reader.ReadLine();
if (line == null)
break;
bag.Add((Point3DCollection)converter.ConvertFrom(line));
// NOTE: could also use this:
//bag.Add(Point3DCollection.Parse(line));
}
while (true);
}
public static void BinarySerialize(this ConcurrentBag<Point3DCollection> bag, Stream stream)
{
if (bag == null)
throw new ArgumentNullException("bag");
if (stream == null)
throw new ArgumentNullException("stream");
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(bag.Count);
foreach (Point3DCollection coll in bag)
{
writer.Write(coll.Count);
foreach (Point3D point in coll)
{
writer.Write(point.X);
writer.Write(point.Y);
writer.Write(point.Z);
}
}
writer.Flush();
}
public static void BinaryDeserialize(this ConcurrentBag<Point3DCollection> bag, Stream stream)
{
if (bag == null)
throw new ArgumentNullException("bag");
if (stream == null)
throw new ArgumentNullException("stream");
BinaryReader reader = new BinaryReader(stream);
int count = reader.ReadInt32();
for (int i = 0; i < count; i++)
{
int pointCount = reader.ReadInt32();
Point3DCollection coll = new Point3DCollection(pointCount);
for (int j = 0; j < pointCount; j++)
{
coll.Add(new Point3D(reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble()));
}
bag.Add(coll);
}
}
}
And a little console app test program to play with:
static void Main(string[] args)
{
Random rand = new Random(Environment.TickCount);
ConcurrentBag<Point3DCollection> bag = new ConcurrentBag<Point3DCollection>();
for (int i = 0; i < 100; i++)
{
Point3DCollection coll = new Point3DCollection();
bag.Add(coll);
for (int j = rand.Next(10); j < rand.Next(100); j++)
{
Point3D point = new Point3D(rand.NextDouble(), rand.NextDouble(), rand.NextDouble());
coll.Add(point);
}
}
using (FileStream stream = new FileStream("test.bin", FileMode.Create))
{
bag.StringSerialize(stream); // or Binary
}
ConcurrentBag<Point3DCollection> newbag = new ConcurrentBag<Point3DCollection>();
using (FileStream stream = new FileStream("test.bin", FileMode.Open))
{
newbag.StringDeserialize(stream); // or Binary
foreach (Point3DCollection coll in newbag)
{
foreach (Point3D point in coll)
{
Console.WriteLine(point);
}
Console.WriteLine();
}
}
}
}
Compression could potentially take advantage of repeated coordinates. Serializers will often use references for repeat objects as well, although I'm not sure there are many set up to work with structs (like Point3D). Anyhow, here are some examples of how to serialize this. To use the standard formatters, you need to convert the data type to something most of them support: list/array. The code below uses Nuget packages NUnit and Json.NET.
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using NUnit.Framework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Media.Media3D;
namespace DemoPoint3DSerialize
{
[TestFixture]
class Tests
{
[Test]
public void DemoBinary()
{
// this shows how to convert them all to strings
var collection = CreateCollection();
var data = collection.Select(c => c.ToArray()).ToList(); // switch to serializable types
var formatter = new BinaryFormatter();
using (var ms = new MemoryStream())
{
formatter.Serialize(ms, data);
Trace.WriteLine("Binary of Array Size: " + ms.Position);
ms.Position = 0;
var dupe = (List<Point3D[]>)formatter.Deserialize(ms);
var result = new ConcurrentBag<Point3DCollection>(dupe.Select(r => new Point3DCollection(r)));
VerifyEquality(collection, result);
}
}
[Test]
public void DemoString()
{
// this shows how to convert them all to strings
var collection = CreateCollection();
IEnumerable<IList<Point3D>> tmp = collection;
var strings = collection.Select(c => c.ToString()).ToList();
Trace.WriteLine("String Size: " + strings.Sum(s => s.Length)); // eh, 2x for Unicode
var result = new ConcurrentBag<Point3DCollection>(strings.Select(r => Point3DCollection.Parse(r)));
VerifyEquality(collection, result);
}
[Test]
public void DemoDeflateString()
{
// this shows how to convert them all to strings
var collection = CreateCollection();
var formatter = new BinaryFormatter(); // not really helping much: could
var strings = collection.Select(c => c.ToString()).ToList();
using (var ms = new MemoryStream())
{
using (var def = new DeflateStream(ms, CompressionLevel.Optimal, true))
{
formatter.Serialize(def, strings);
}
Trace.WriteLine("Deflate Size: " + ms.Position);
ms.Position = 0;
using (var def = new DeflateStream(ms, CompressionMode.Decompress))
{
var stringsDupe = (IList<string>)formatter.Deserialize(def);
var result = new ConcurrentBag<Point3DCollection>(stringsDupe.Select(r => Point3DCollection.Parse(r)));
VerifyEquality(collection, result);
}
}
}
[Test]
public void DemoStraightJson()
{
// this uses Json.NET
var collection = CreateCollection();
var formatter = new JsonSerializer();
using (var ms = new MemoryStream())
{
using (var stream = new StreamWriter(ms, new UTF8Encoding(true), 2048, true))
using (var writer = new JsonTextWriter(stream))
{
formatter.Serialize(writer, collection);
}
Trace.WriteLine("JSON Size: " + ms.Position);
ms.Position = 0;
using (var stream = new StreamReader(ms))
using (var reader = new JsonTextReader(stream))
{
var result = formatter.Deserialize<List<Point3DCollection>>(reader);
VerifyEquality(collection, new ConcurrentBag<Point3DCollection>(result));
}
}
}
[Test]
public void DemoBsonOfArray()
{
// this uses Json.NET
var collection = CreateCollection();
var formatter = new JsonSerializer();
using (var ms = new MemoryStream())
{
using (var stream = new BinaryWriter(ms, new UTF8Encoding(true), true))
using (var writer = new BsonWriter(stream))
{
formatter.Serialize(writer, collection);
}
Trace.WriteLine("BSON Size: " + ms.Position);
ms.Position = 0;
using (var stream = new BinaryReader(ms))
using (var reader = new BsonReader(stream, true, DateTimeKind.Unspecified))
{
var result = formatter.Deserialize<List<Point3DCollection>>(reader); // doesn't seem to read out that concurrentBag
VerifyEquality(collection, new ConcurrentBag<Point3DCollection>(result));
}
}
}
private ConcurrentBag<Point3DCollection> CreateCollection()
{
var rand = new Random(42);
var bag = new ConcurrentBag<Point3DCollection>();
for (int i = 0; i < 10; i++)
{
var collection = new Point3DCollection();
for (int j = 0; j < i + 10; j++)
{
var point = new Point3D(rand.NextDouble(), rand.NextDouble(), rand.NextDouble());
collection.Add(point);
}
bag.Add(collection);
}
return bag;
}
private class CollectionComparer : IEqualityComparer<Point3DCollection>
{
public bool Equals(Point3DCollection x, Point3DCollection y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(Point3DCollection obj)
{
return obj.GetHashCode();
}
}
private void VerifyEquality(ConcurrentBag<Point3DCollection> collection, ConcurrentBag<Point3DCollection> result)
{
var first = collection.OrderBy(c => c.Count);
var second = collection.OrderBy(c => c.Count);
first.SequenceEqual(second, new CollectionComparer());
}
}
}
Use Google's protobuf-net. protobuf-net is an open source .net implementation of Google's protocol buffer binary serialization format which can be used as a replacement for the BinaryFormatter serializer. It is probably going to be the fastest solution and easiest to implement.
Here is a link to the the main google wiki for protobuf-net. On the left, you'll find the downloads for all of the most updated binaries.
https://code.google.com/p/protobuf-net/
Here is a great article that you might want to look at first to get a feel for how it works.
http://wallaceturner.com/serialization-with-protobuf-net
Here is a link to a discussion on google's wiki about your specific problem. The answer is at the bottom of the page. That's where I got the code below and substituted with details from your post.
https://code.google.com/p/protobuf-net/issues/detail?id=354
I haven't used it myself but it looks like a very good solution to your stated needs. From what I gather, your code would end up some variation of this.
[ProtoContract]
public class MyClass {
public ConcurrentQueue<Point3DCollection> Points {get;set;}
[ProtoMember(1)]
private Point3DCollection[] Items
{
get { return Points.ToArray(); }
set { Items = new ConcurrentBag<Point3DCollection>(value); }
}
}
I wish you the best of luck. Take care.
For a large amount of data, why don't you consider Sqlite or any other small database system etc, which can store structured data in the file.
I have seen many 3d programs using database to store structure along with relations, which allow them to partially insert/update/delete data.
Benefit of Sqlite/database will be multithreaded serialization to improve speed, however you need to do little bit of work on sqlite to enable multi threaded sqlite connection, or else you can use LocalDB of SQL Express or even Sql Compact.
Also some of workload of loading data can be done through queries, which will be indexed by database nicely. And most of things can be done on background worker without interfering with User Interface.
Sqlite has limited multi-thread support, which can be explored here http://www.sqlite.org/threadsafe.html
Sql Compact is thread safe and requires installation that can be installed without admin priviledges. And you can use Entity framework as well.

Operation not permitted on IsolatedStorageFileStream in WP8

I have seen numerous similar questions and I have really tried all the solutions but none seems to work for me.
This is what I have now:
private readonly object _lock = new object();
List<DataModel> dataList = new List<DataModel>();
lock (_lock)
{
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
try
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("Data.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<DataModel>));
dataList = (List<DataModel>)serializer.Deserialize(stream);
}
}
catch (IsolatedStorageException e) { e.ToString(); }
}
}
The error occurs on the line using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("Data.xml", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)).
I deduced from the other solutions that I read that this error mainly occurs when I want to write to the file while others are reading it or execute that code block several times concurrently, I end up locking the file."
With the presence of the lock, FileAccess.ReadWrite and FileShare.ReadWrite statements, I'm pretty sure something else is throwing that exception.
My question is what could be throwing the exception (IsolatedStorageException) and how do I take care of it?
There is no InnerException on this one.
Edit: Upon Kookiz suggestion, I'm including this code lines
First, I create my .xml file like this:
public static void createDataXML()
{
List<DataModel> dataList = new List<DataModel>();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists("Data.xml"))
{ return; }
using (IsolatedStorageFileStream stream = myIsolatedStorage.CreateFile("Data.xml"))
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(List<DataModel>));
serializer.Serialize(stream, dataList);
}
catch
{ }
}
}
}
Later, I populate with this code:
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("Data.xml", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<DataModel>));
using (XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, dataList);
}
}
}
I know this is a less conventional way of doing this but it IS a way. I was attempting to do something else and this was the result. Just add in the known type you need and this will work
[DataContractAttribute]
[KnownType (typeof(List<String>))]
public class SerializableObject
{
[DataMember]
public List<String> serFile { get; set; }
}
public static Object GetFile(String FileName)
{
try
{
if (!IsolatedStorageFile.GetUserStoreForApplication().FileExists(FileName))
{
throw new System.ArgumentException("File Doesn't Exist In Isoloated Storage");
}
}
catch { return null; }
Object ret = new Object();
try
{
IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile(#"\" + FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
SerializableObject serList = new SerializableObject();
DataContractSerializer dsc = new DataContractSerializer(serList.GetType());
ret = ((SerializableObject)dsc.ReadObject(fileStream)).serFile;
}
catch (Exception error) { throw new System.ArgumentException(error.Message); }
return ret;
}
The implied task here is that you need to serialize it within a SerializableObject instance. Let me know if you need that code also
Edit
As promised, the savefile function
public static void SaveFile(String FileName, List<String> File)
{
try
{
if (FileName.Length < 1)
{
throw new System.ArgumentException("File Name Must Not Be Empty");
}
if (IsolatedStorageFile.GetUserStoreForApplication().AvailableFreeSpace <= 0)
{
throw new System.ArgumentException("Isolated Storage Out of Memory - Please free up space.");
}
if (IsolatedStorageFile.GetUserStoreForApplication().FileExists(FileName))
{
throw new System.ArgumentException("File Already Exists - Please choose a unique name.");
}
if (File == null)
{
throw new System.ArgumentException("Cannot Save Null Files");
}
}
catch (Exception e)
{
return;
}
try
{
SerializableObject so = new SerializableObject() { serFile = File };
DataContractSerializer dsc = new DataContractSerializer(so.GetType());
IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
StreamWriter writer;
writer = new StreamWriter(new IsolatedStorageFileStream(FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, file));
dsc.WriteObject(writer.BaseStream, so);
}
catch (Exception error) { throw new System.ArgumentException(error.Message); }
}
Enjoy serializing!
My dear, though this exception of Operation Not Permitted On IsolatedStorage is mainly due to ReadWrite by another thread, in your case it is because of ONE SIMPLE REASON
FileMode.Open throws exception when the filename DOES NOT EXIST
Try with FileMode.OpenOrCreate and it will work like a charm
List<DataModel> dataList = new List<DataModel>();
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile("Data.xml", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<DataModel>));
dataList = (List<DataModel>)serializer.Deserialize(stream);
}
}

Read\write to a file: heavily used application

I had asked a question here: The process cannot access the file because it is being used by another process: Correct way to read\write to a file: heavily used application-Part II.
We have a heavily used .Net 3.5 application that reads "expensive to create" data and caches it. However we are getting a lot of errors around both reading the cache file and writing to the cache file. I have a single process, multiple threads and I want the application to synchronize access to a resource. I was advised to use a simple locking mechanism like lock or ReaderWriterLockSlim (see: http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx). This however seems to have made the problem much much worse in production.
EDIT
After the change was implemented, a lot of the cache files have a ">" tag in the end. Due to this the files are no longer xml files.
Can someone look at the code and advise what could I be doing wrong?
Code before change:
private XmlDocument ReadFromFile()
{
XmlDocument result=null;
string fileSystemPath=FileSystemPath();
try
{
result=new XmlDocument();
using (StreamReader streamReader = new StreamReader(fileSystemPath))
{
result.Load(streamReader);
}
}
catch (FileNotFoundException)
{
result=null;
}
return result;
}
private object thisObject= new object();
private void WriteToFile(string siteID, XmlDocument stuff)
{
string fileSystemPath=FileSystemPath();
lock(thisObject)
{
using (StreamWriter streamWriter = new StreamWriter(fileSystemPath))
{
stuff.Save(streamWriter);
}
}
}
Code after change:
private readonly ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim();
private XmlDocument ReadFromFile()
{
XmlDocument result = null;
var fileSystemPath = FileSystemPath();
readerWriterLockSlim.EnterReadLock();
try
{
result = new XmlDocument();
using (var fileStream = new FileStream(fileSystemPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var streamReader = new StreamReader(fileStream))
{
result.Load(streamReader);
}
}
catch (FileNotFoundException)
{
result = null;
}
finally
{
readerWriterLockSlim.ExitReadLock();
}
return result;
}
private void WriteToFile()
{
var fileSystemPath = FileSystemPath();
readerWriterLockSlim.EnterWriteLock();
try
{
using (var fileStream = new FileStream(fileSystemPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
using (var streamWriter = new StreamWriter(fileStream))
{
stuff.Save(streamWriter);
}
}
finally
{
readerWriterLockSlim.ExitWriteLock();
}
}
This code with some little changes should work
private readonly ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim();
private XmlDocument ReadFromFile()
{
XmlDocument result = null;
var fileSystemPath = FileSystemPath();
readerWriterLockSlim.EnterReadLock();
try
{
result = new XmlDocument();
using (var streamReader = new StreamReader(fileStream))
{
result.Load(streamReader);
}
}
catch (FileNotFoundException)
{
result = null;
}
finally
{
readerWriterLockSlim.ExitReadLock();
}
return result;
}
private void WriteToFile(XmlDocument stuff)
{
var fileSystemPath = FileSystemPath();
readerWriterLockSlim.EnterWriteLock();
try
{
using (var streamWriter = new StreamWriter(fileSystemPath))
{
stuff.Save(streamWriter);
}
}
finally
{
readerWriterLockSlim.ExitWriteLock();
}
}

How to keep XmlSerializer from killing NewLines in Strings?

Suppose I have a simple Class with just one Member a String.
public class Abc
{
private String text;
public String Text
{
get { return this.text; }
set { this.text = value; }
}
}
Now when I serialize and then deserialize it with the questionable XmlSerializer any text containing newlines ('\r\n' or Environment.NewLine) are transformed to '\n'.
How do I keep the newlines?
It is not the XmlSerializer but the XmlWriter which is removing your CR. To retain it we must have the writer convert CR to its character entity 
.
XmlWriterSettings ws = new XmlWriterSettings();
ws.NewLineHandling = NewLineHandling.Entitize;
XmlSerializer ser = new XmlSerializer( typeof( Abc ) );
using (XmlWriter wr = XmlWriter.Create( "abc.xml", ws )) {
ser.Serialize( wr, s );
}
This is exactly the same with DataContractSerializer:
var ser = new DataContractSerializer( typeof( Abc ) );
using (XmlWriter wr = XmlWriter.Create( "abc.xml", ws )) {
ser.Serialize( wr, s );
}
Why do we need to do this?
This is because compliant XML parsers must, before parsing, translate CRLF and any CR not followed by a LF to a single LF. This behavior is defined in the End-of-Line handling section of the XML 1.0 specification.
As this happens before parsing, you need to encode CR as its character entity if you want the CR to exist in the document.
public class SerializeAny<TF> where TF : new()
{
public static TF Deserialize(string serializedData)
{
try
{
var xmlSerializer = new XmlSerializer(typeof(TF));
TF collection;
using (var xmlReader = new XmlTextReader(serializedData, XmlNodeType.Document, null))
{
collection = (TF)xmlSerializer.Deserialize(xmlReader);
}
return collection;
}
catch (Exception)
{
}
return new TF();
}
public static TF DeserializeZip(string path)
{
try
{
var bytes = File.ReadAllBytes(path);
string serializedData = Unzip(bytes);
TF collection = Deserialize(serializedData);
return collection;
}
catch (Exception)
{
}
return new TF();
}
public static string Serialize(TF options)
{
var xml = "";
try
{
var xmlSerializer = new XmlSerializer(typeof(TF));
using (var stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, options);
xml = stringWriter.ToString();
}
}
catch (Exception ex)
{
return ex.Message;
}
return xml;
}
public static string SerializeZip(TF options, string path)
{
var xml = "";
try
{
xml = Serialize(options);
var zip = Zip(xml);
File.WriteAllBytes(path, zip);
}
catch (Exception ex)
{
return ex.Message;
}
return xml;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
internal static String SerializeObject<T>(T obj, Encoding enc)
{
using (var ms = new MemoryStream())
{
var xmlWriterSettings = new System.Xml.XmlWriterSettings()
{
// If set to true XmlWriter would close MemoryStream automatically and using would then do double dispose
// Code analysis does not understand that. That's why there is a suppress message.
CloseOutput = false,
Encoding = enc,
OmitXmlDeclaration = false,
Indent = true
};
using (var xw = XmlWriter.Create(ms, xmlWriterSettings))
{
var s = new XmlSerializer(typeof(T));
s.Serialize(xw, obj);
}
return enc.GetString(ms.ToArray());
}
}
private static void CopyTo(Stream src, Stream dest)
{
byte[] bytes = new byte[4096];
int cnt;
while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0)
{
dest.Write(bytes, 0, cnt);
}
}
private static byte[] Zip(string str)
{
var bytes = Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(mso, CompressionMode.Compress))
{
//msi.CopyTo(gs);
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
private static string Unzip(byte[] bytes)
{
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(msi, CompressionMode.Decompress))
{
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
}
}
public class BinarySerialize<T> where T : new()
{
public static string Serialize(T options, string path)
{
var xml = "";
try
{
File.Delete(path);
}
catch (Exception)
{
}
try
{
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
var bf = new BinaryFormatter();
bf.Serialize(fs, options);
}
}
catch (Exception ex)
{
return ex.Message;
}
return xml;
}
public static T Deserialize(string path)
{
T filteroptions;
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var bf = new BinaryFormatter();
filteroptions = (T)bf.Deserialize(fs);
}
return filteroptions;
}
}
Use this code:
public static FilterOptions Deserialize(string serializedData)
{
try
{
var xmlSerializer = new XmlSerializer(typeof(FilterOptions));
var xmlReader = new XmlTextReader(serializedData,XmlNodeType.Document,null);
var collection = (FilterOptions)xmlSerializer.Deserialize(xmlReader);
return collection;
}
catch (Exception)
{
}
return new FilterOptions();
}
Nice solution, Lachlan Roche!
The function below (in VB.NET) uses a StringWriter to return a String, rather than writing the result to a file using an XmlWriter.
''' <summary>
''' Exports the object data to an XML formatted string.
''' Maintains CR characters after deserialization.
''' The object must be serializable to work.
''' </summary>
Public Function ExportObjectXml(ByVal obj As Object) As String
If obj Is Nothing Then
Return String.Empty
End If
Dim serializer As New XmlSerializer(obj.GetType)
Dim settings As New XmlWriterSettings With {.NewLineHandling = NewLineHandling.Entitize}
Using output As New StringWriter
Using writer As XmlWriter = XmlWriter.Create(output, settings)
serializer.Serialize(writer, obj)
Return output.ToString
End Using
End Using
End Function

Categories

Resources