Custom Serialization and Deserialization failing on Stream Test cases - c#

I'm trying to test my custom serialization and deserializaton logic with memory stream by writing the test cases but unable to test is because of this error while reading the stream during deserializaton.
EndOfStreamException Unable to read beyond the end of the stream
Am I using memory stream correctly or should I use some other stream? sharing the code:
[TestClass]
public class SerialTest
{
[TestMethod]
public void SerializationTestUsingStream()
{
Employee emp = new Employee(20);
MemoryStream stream = new MemoryStream();
this.SerializeEmployee(stream, emp);
StreamReader reader = new StreamReader(stream);
string text = reader.ReadToEnd(); // shows Empty string ""
var newEmp = this.DeserializeEmployee(stream);
emp.Should().Equals(newEmp);
}
private void SerializeEmployee(Stream stream, Employee collection)
{
using (BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true))
{
writer.Write(collection.age);
}
}
private Employee DeserializeEmployee(Stream stream)
{
using (var reader = new BinaryReader(stream, Encoding.UTF8, true))
{
int age = reader.ReadInt32(); // Exception Comes here while reading from the stream
return new Employee(age);
}
}
internal class Employee
{
public Employee(int age)
{
this.age = age;
}
public int age { get; set; }
}
}

You need to reset the position of the stream before read it.
stream.Position = 0L;

Related

How to serialize different objects to the same XML file and then deserialize

When a user uses my program, it will generate many thousands of objects over the course of a couple of hours. These cannot accumulate in RAM, so I want to write them to a single file as they are generated. Then, in a different program, the objects must all be deserialized.
When I try to serialize different objects of the same class to the same XML file and then try to deserialize later, I get:
System.InvalidOperationException: 'There is an error in XML document (1, 206).'
Inner Exception
XmlException: There are multiple root elements. Line 1, position 206.
Here is an example of a .NET 6.0 console app that recapitulates this problem:
using System.Xml.Serialization;
using System.IO;
using System.Xml;
namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
// Serialize a person
using (FileStream stream = new FileStream("people.xml", FileMode.Create))
{
Person jacob = new Person { Name = "Jacob", Age = 33, Alive = true };
XmlSerializer serializer = new XmlSerializer(typeof(Person));
serializer.Serialize(stream, jacob);
}
// Serialize another person to the same file using the "clean XML" method
// https://stackoverflow.com/questions/1772004/how-can-i-make-the-xmlserializer-only-serialize-plain-xml
using (StreamWriter stream = new StreamWriter("people.xml", true))
{
Person rebecca = new Person { Name = "Rebecca", Age = 45, Alive = true };
stream.Write(SerializeToString(rebecca));
}
// Deserialize the people
List<Person> people = new List<Person>();
using (FileStream stream = new FileStream("people.xml", FileMode.Open))
{
XmlSerializer deserializer = new XmlSerializer(typeof(Person));
while (stream.Position < stream.Length)
{
people.Add((Person)deserializer.Deserialize(stream));
}
}
// See the people
foreach (Person person in people)
Console.WriteLine($"Hello. I am {person.Name}. I am {person.Age} and it is {person.Alive} that I am alive.");
}
// Serialize To Clean XML
public static string SerializeToString<T>(T value)
{
var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var serializer = new XmlSerializer(value.GetType());
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, value, emptyNamespaces);
return stream.ToString();
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool Alive { get; set; }
public Person()
{
Name = "";
}
}
}
Probably not the perfect answer, but could you de-serialize to a list, append your new person, and then re-serialize?
public static List<Person> ReadPeople(string file)
{
var people = new List<Person>();
if (File.Exists(file))
{
var serializer = new XmlSerializer(typeof(List<Person>));
using (var stream = new FileStream(file, FileMode.Open))
{
people = (List<Person>)serializer.Deserialize(stream);
}
}
return people;
}
public static void SavePerson(string file, Person person)
{
var people = ReadPeople(file);
people.Add(person);
var serializer = new XmlSerializer(typeof(List<Person>));
using (var stream = new FileStream(file, FileMode.Create))
{
serializer.Serialize(stream, people);
}
}
Additionally, on the "FileMode.Create", is the "FileMode.Append" option. The problem with that is it will append lists next to each other, rather than nesting the "people"
NB - I've split this into two functions to give the flexibility of being able to load the file easily

Windows 8 phone Saving and loading a list of Objects

I would like to save and load my History list filled with History Entry objects. I am trying to do this through Isolated Storage, so that when the user opens and closes the app none of their browsing history is lost. It is saved which can be loaded once the app is clicked. I have had a look around and saw this question on stackoverflow, and I have tried to follow it but came across so errors. Isolated Storage & Saving Multiple Objects.
Here is the code
The HistoryEntry class
public string URL { get; set; }
public string timestamp { get; set; }
public string date { get; set; }
The MainPage code:
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.IO;
List<HistoryEntry> urls = new List<HistoryEntry>();
public HistoryEntry selectedHistory;
public MainPage()
{
InitializeComponent();
Deserialize<>(urls, ???);
}
void Browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
HistoryEntry urlObj = new HistoryEntry();
urlObj.URL = url;
urlObj.timestamp = DateTime.Now.ToString("HH:mm");
urlObj.date = url.Remove(url.LastIndexOf('.'));
urls.Add(urlObj);
textBox1.Text = url;
listBox.ItemsSource = null;
listBox.ItemsSource = urls;
Serialize(urlObj, urls);
}
private static void Serialize(string fileName, object source)
{
var userStore = IsolatedStorageFile.GetUserStoreForApplication();
using (var stream = new IsolatedStorageFileStream(fileName, FileMode.Create, userStore))
{
XmlSerializer serializer = new XmlSerializer(source.GetType());
serializer.Serialize(stream, source);
}
}
public static void Deserialize<T>(ObservableCollection<T> list, string filename)
{
list = new ObservableCollection<T>();
var userStore = IsolatedStorageFile.GetUserStoreForApplication();
if (userStore.FileExists(filename))
{
using (var stream = new IsolatedStorageFileStream(filename, FileMode.Open, userStore))
{
XmlSerializer serializer = new XmlSerializer(list.GetType());
var items = (ObservableCollection<T>)serializer.Deserialize(stream);
foreach (T item in items)
{
list.Add(item);
}
}
}
}
Serialize has some invalid arguments which is the same with when De serialize is called. What are the appropriate values to be sent to the method, and will this successfully save and load the history objects.
Thank you in advance :)
If you need any more details please comment and I will be happy to explain in further detail :)
Did you try this way using MemoryStream which it did work for Win 8 :
To Serialize:
MemoryStream sessionData = new MemoryStream();
DataContractSerializer serializer = new
DataContractSerializer(typeof(ObservableCollection<NewsByTag>));
serializer.WriteObject(sessionData, data);
StorageFile file = await ApplicationData.Current.LocalFolder
.CreateFileAsync(sFileName);
using (Stream fileStream = await file.OpenStreamForWriteAsync())
{
sessionData.Seek(0, SeekOrigin.Begin);
await sessionData.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
To Deserialize it back:
StorageFile file = await ApplicationData.Current.LocalFolder.
GetFileAsync(sFileName);
using (IInputStream inStream = await file.OpenSequentialReadAsync())
{
DataContractSerializer serializer =
new DataContractSerializer(typeof(ObservableCollection<NewsByTag>));
var data = (ObservableCollection<NewsByTag>)serializer
.ReadObject(inStream.AsStreamForRead());
}
Hope it helps!

Taglib-sharp: how to use the IFileAbstraction to allow reading metadata from stream?

I'm trying to read the metadata of a mp3 file stored in IsolatedStorage using TagLib.
I know TagLib normally only take a file path as input but as WP uses a sandbox environment I need to use a stream.
Following this tutorial (http://www.geekchamp.com/articles/reading-and-writing-metadata-tags-with-taglib) I created a iFileAbstraction interface:
public class SimpleFile
{
public SimpleFile(string Name, Stream Stream)
{
this.Name = Name;
this.Stream = Stream;
}
public string Name { get; set; }
public Stream Stream { get; set; }
}
public class SimpleFileAbstraction : TagLib.File.IFileAbstraction
{
private SimpleFile file;
public SimpleFileAbstraction(SimpleFile file)
{
this.file = file;
}
public string Name
{
get { return file.Name; }
}
public System.IO.Stream ReadStream
{
get { return file.Stream; }
}
public System.IO.Stream WriteStream
{
get { return file.Stream; }
}
public void CloseStream(System.IO.Stream stream)
{
stream.Position = 0;
}
}
Normally I would now be able to do this:
using (IsolatedStorageFileStream filestream = new IsolatedStorageFileStream(name, FileMode.OpenOrCreate, FileAccess.ReadWrite, store))
{
filestream.Write(data, 0, data.Length);
// read id3 tags and add
SimpleFile newfile = new SimpleFile(name, filestream);
TagLib.Tag tags = TagLib.File.Create(newfile);
}
The problem is that TagLib.File.Create still doesn't want to accept the SimpleFile object.
How do I make this work?
Your code doesn't compile because TagLib.File.Create wants IFileAbstraction on input, and you're giving it SimpleFile instance which doesn't implement the interface. Here's one way to fix:
// read id3 tags and add
SimpleFile file1 = new SimpleFile( name, filestream );
SimpleFileAbstraction file2 = new SimpleFileAbstraction( file1 );
TagLib.Tag tags = TagLib.File.Create( file2 );
Don't ask me why we need SimpleFile class instead of passing name and stream into SimpleFileAbstraction - it was in your sample.

CsvHelper not writing anything to memory stream

I have the following method:
public byte[] WriteCsvWithHeaderToMemory<T>(IEnumerable<T> records) where T : class
{
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
csvWriter.WriteRecords<T>(records);
return memoryStream.ToArray();
}
}
Which is being called with a list of objects - eventually from a database, but since something is not working I'm just populating a static collection. The objects being passed are as follows:
using CsvHelper.Configuration;
namespace Application.Models.ViewModels
{
public class Model
{
[CsvField(Name = "Field 1", Ignore = false)]
public string Field1 { get; set; }
[CsvField(Name = "Statistic 1", Ignore = false)]
public int Stat1{ get; set; }
[CsvField(Name = "Statistic 2", Ignore = false)]
public int Stat2{ get; set; }
[CsvField(Name = "Statistic 3", Ignore = false)]
public int Stat3{ get; set; }
[CsvField(Name = "Statistic 4", Ignore = false)]
public int Stat4{ get; set; }
}
}
What I'm trying to do is write a collection to a csv for download in an MVC application. Every time I try to write to the method though, the MemoryStream is coming back with zero length and nothing being passed to it. I've used this before, but for some reason it's just not working - I'm somewhat confused. Can anyone point out to me what I've done wrong here?
Cheers
You already have a using block which is great. That will flush your writer for you. You can just change your code slightly for it to work.
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
csvWriter.WriteRecords<T>(records);
} // StreamWriter gets flushed here.
return memoryStream.ToArray();
}
If you turn AutoFlush on, you need to be careful. This will flush after every write. If your stream is a network stream and over the wire, it will be very slow.
Put csvWriter.Flush(); before you return to flush the writer/stream.
EDIT: Per Jack's response. It should be the stream that gets flushed, not the csvWriter. streamWriter.Flush();. Leaving original solution, but adding this correction.
EDIT 2: My preferred answer is: https://stackoverflow.com/a/22997765/1795053 Let the using statements do the heavy lifting for you
Putting all these together (and the comments for corrections), including resetting the memory stream position, the final solution for me was;
using (MemoryStream ms = new MemoryStream())
{
using (TextWriter tw = new StreamWriter(ms))
using (CsvWriter csv = new CsvWriter(tw, CultureInfo.InvariantCulture))
{
csv.WriteRecords(errors); // Converts error records to CSV
tw.Flush(); // flush the buffered text to stream
ms.Seek(0, SeekOrigin.Begin); // reset stream position
Attachment a = new Attachment(ms, "errors.csv"); // Create attachment from the stream
// I sent an email here with the csv attached.
}
}
In case the helps someone else!
There is no flush in csvWriter, the flush is in the streamWriter. When called
csvWriter.Dispose();
it will flush the stream.
Another approach is to set
streamWriter.AutoFlush = true;
which will automatically flush the stream every time.
Here is working example:
void Main()
{
var records = new List<dynamic>{
new { Id = 1, Name = "one" },
new { Id = 2, Name = "two" },
};
Console.WriteLine(records.ToCsv());
}
public static class Extensions {
public static string ToCsv<T>(this IEnumerable<T> collection)
{
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
csvWriter.WriteRecords(collection);
} // StreamWriter gets flushed here.
return Encoding.ASCII.GetString(memoryStream.ToArray());
}
}
}
Based on this answer.
using CsvHelper;
public class TwentyFoursStock
{
[Name("sellerSku")]
public string ProductSellerSku { get; set; }
[Name("shippingPoint")]
public string ProductShippingPoint { get; set; }
}
using (var writer = new StreamWriter("file.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(TwentyFoursStock);
}

C# XML Serialize object of Array object "There was an error generating the XML document."

I'm having a problem with the c# XML serialization system it's throws an Ambigus exception,
There was an error generating the XML document.
Now i have a Class that contains refferences to other classes and arrays of the other classes
E.G
namespace P2PFileLayout
{
public class p2pfile
{
public FileList FileList;
public StatusServer StatusServer;
public String Hash;
}
}
namespace P2PFileLayout.parts
{
public class StatusServer
{
public Auth Auth;
public Servers Servers;
}
public class Servers
{
public Server[] Server;
}
public class Server
{
public bool AuthRequired = false;
public string Address;
}
public class Files
{
public File[] File;
}
public class File
{
public string FileName = "";
public int BlockSize = 0;
public int BlockCount = 0;
}
public class Directory
{
public string Name;
public Files Files;
public Directory[] Dir;
}
public class Auth
{
public AuthServer[] AuthServer;
}
public class FileList
{
public Files Files;
public Directory[] Directory;
}
}
My Example data
// create the test file
testFile = new p2pfile();
// create a fake fileList
testFile.FileList = new P2PFileLayout.parts.FileList();
testFile.FileList.Directory = new P2PFileLayout.parts.Directory[1];
testFile.FileList.Directory[0] = new P2PFileLayout.parts.Directory();
testFile.FileList.Directory[0].Name = "testFolder";
testFile.FileList.Directory[0].Files = new P2PFileLayout.parts.Files();
testFile.FileList.Directory[0].Files.File = new P2PFileLayout.parts.File[2];
testFile.FileList.Directory[0].Files.File[0] = new P2PFileLayout.parts.File();
testFile.FileList.Directory[0].Files.File[0].FileName = "test.txt";
testFile.FileList.Directory[0].Files.File[0].BlockSize = 64;
testFile.FileList.Directory[0].Files.File[0].BlockCount = 1;
testFile.FileList.Directory[0].Files.File[1] = new P2PFileLayout.parts.File();
testFile.FileList.Directory[0].Files.File[1].FileName = "test2.txt";
testFile.FileList.Directory[0].Files.File[1].BlockSize = 64;
testFile.FileList.Directory[0].Files.File[1].BlockCount = 1;
// create a fake status server
testFile.StatusServer = new P2PFileLayout.parts.StatusServer();
testFile.StatusServer.Servers = new P2PFileLayout.parts.Servers();
testFile.StatusServer.Servers.Server = new P2PFileLayout.parts.Server[1];
testFile.StatusServer.Servers.Server[0] = new P2PFileLayout.parts.Server();
testFile.StatusServer.Servers.Server[0].Address = "http://localhost:8088/list.php";
// create a file hash (real)
HashGenerator.P2PHash hashGen = new HashGenerator.P2PHash();
testFile.Hash = hashGen.getHash();
treeView1.Nodes.Add(new TreeNode("Loading..."));
Classes.CreateTreeView ctv = new Classes.CreateTreeView();
ctv.BuildTreeView(testFile.FileList, treeView1);
treeView1.AfterCheck += new TreeViewEventHandler(treeView1_AfterCheck);
Now that is not as complicated as mine in terms of dept as my i loop objects so dir has support for more dirs but thats just an example
Then i'm serializing to a string var as i want to do a little more than just serialize it but here is my serialization
private string ToXml(object Obj, System.Type ObjType)
{
// instansiate the xml serializer object
XmlSerializer ser = new XmlSerializer(ObjType);
// create a memory stream for XMLTextWriter to use
MemoryStream memStream = new MemoryStream();
// create an XML writer using our memory stream
XmlTextWriter xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
// write the object though the XML serializer method using the W3C namespaces
ser.Serialize(xmlWriter, Obj);
// close the XMLWriter
xmlWriter.Close();
// close the memoryStream
memStream.Close();
// get the string from the memory Stream
string xml = Encoding.UTF8.GetString(memStream.GetBuffer());
// remove the stuff before the xml code we care about
xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
// clear any thing at the end of the elements we care about
xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
// return the XML string
return xml;
}
Can any one see why this is not working or any clues as to why it would not work normally
Why are you doing it manually?
What about this approach?
http://support.microsoft.com/kb/815813
Test test = new Test() { Test1 = "1", Test2 = "3" };
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(test.GetType());
MemoryStream ms = new MemoryStream();
x.Serialize(ms, test);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string xml = sr.ReadToEnd();
As for the ambigous message, take a look into the inner exceptions. XML serialization errors use innerexception a lot, and you usually have to look to all levels of InnerExceptions to know what is really happening.

Categories

Resources