Deserializing a byte[] back into a DataTable - c#

I have the following code to serialize /deserialize a DataTable:
public static byte[] Serialize(DataTable dt)
{
System.IO.MemoryStream stream = new System.IO.MemoryStream();
System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
formatter.Serialize(stream, dt);
return stream.GetBuffer();
}
public static DataTable Deserialize(byte[] buffer)
{
System.IO.MemoryStream stream = new System.IO.MemoryStream(buffer);
System.Runtime.Serialization.IFormatter formatter = new BinaryFormatter();
return formatter.Deserialize(stream) as DataTable;
}
The serialize method works fine but the deserialize method produces this error:
The input stream is not a valid binary format. The starting contents (in bytes) are: 1F-8B-08 ...
I am 99% sure I have gotten this method to work in the past, not sure whats wrong.

you should not use GetBuffer() but ToArray() since the latter returns really the content while Getbuffer() could return uninitialized bytes...
see
http://msdn.microsoft.com/en-us/library/system.io.memorystream.toarray.aspx
http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer.aspx

Related

Fastest way to save/load enum[] (byte) to a file

The array has duplicate elements and their order is important (must be kept). I have to save/load hundreds of these files constantly and each file may hold an array up to 100,000 elements.
The code bellow is an example of what I'm currently doing to save/load the files. Since IO is slow I got a significant speed improvement by casting the enums to byte before serialization (reducing the file size by 10x). I'm not sure I should be using BinaryFormatter though.
I'm still looking for improvements as everything should be as quick as possible, is there a better alternative to what I'm currently doing? How would you do it?
enum DogBreed : byte { Bulldog, Poodle, Beagle, Rottweiler, Chihuahua }
DogBreed[] myDogs = { DogBreed.Beagle, DogBreed.Poodle, DogBreed.Beagle, DogBreed.Bulldog };
public void Save(string path)
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Create);
byte[] myDogsInByte = Array.ConvertAll(myDogs, new Converter<DogBreed, byte>(DogBreedToByte));
formatter.Serialize(stream, myDogsInByte);
stream.Close();
}
public bool Load(string path)
{
if (!File.Exists(path))
{
return false;
}
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(path, FileMode.Open);
byte[] myDogsInByte = formatter.Deserialize(stream) as byte[];
myDogs = Array.ConvertAll(myDogsInByte, new Converter<byte, DogBreed>(ByteToDogBreed));
stream.Close();
return true;
}
private byte DogBreedToByte(DogBreed db)
{
return (byte)db;
}
private DogBreed ByteToDogBreed(byte bt)
{
return (DogBreed)bt;
}
EDIT: New code based on Jeremy suggestion, the code is working, I'll try to test the performance of it and post the results here as soon as I can.
enum DogBreed : byte { Bulldog, Poodle, Beagle, Rottweiler, Chihuahua }
DogBreed[] myDogs = { DogBreed.Beagle, DogBreed.Poodle, DogBreed.Beagle, DogBreed.Bulldog };
public void Save(string path)
{
byte[] myDogsInByte = new byte[myDogs.Length];
Array.Copy(myDogs,myDogsInByte,myDogs.Length);
File.WriteAllBytes(path, myDogsInByte);
}
public bool Load(string path)
{
if (!File.Exists(path))
{
return false;
}
byte[] myDogsInByte = File.ReadAllBytes(path);
myDogs = (DogBreed[])(object)myDogsInByte;
return true;
}
While the C# compiler will complain if you attempt to directly assign a byte[] to an enum array. The runtime doesn't care.
var bytes = File.ReadAllBytes(path);
myDogs = (DogBreed[])(object)bytes;
The VS debugger will show that myDogs is really a byte array, but accessing an element from the array works just fine.
Update;
ArgumentException: Object must be an array of primitives.
So File.WriteAllBytes() doesn't like being tricked with an enum[]. You should be able to to use Array.Copy to quickly duplicate the enum values into a byte[].
var buffer = new byte[myDogs.Length];
Array.Copy(myDogs, buffer, myDogs.Length);
File.WriteAllBytes(path, buffer);
Of course that's not a free operation, but it should be fairly fast even for large arrays.

Why are my hex strings exactly the same on the server. But are different objects when deserialized

After serializing a object in C# I upload it to my database, each of the hex strings look like this:
0x0001000000FFFFFFFF01000000000000000C0200000046496E76656E746F72794F626A6563742C2056657273696F6E3D312E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D6E756C6C050100000019496E76656E746F72792E496E76656E746F72794F626A65637404000000
I was expecting each of the objects to be the same (meaning a error in my code) when I deserialized .But they are not, instead they represent the correct unique objects.
Is there some kind of pointer association happening in sql server?
Serialize method:
public byte[] serialize()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this); //NOTE: this refers to a InventoryObject
byte[] returnVal = ms.ToArray();
ms.Close();
return returnVal;
}
Deserialize method:
public static InventoryObject deserialize(byte[] arrBytes)
{
MemoryStream memStream = new MemoryStream();
BinaryFormatter binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
InventoryObject obj = (Object)binForm.Deserialize(memStream) as InventoryObject;
return obj ;
}
I figured it out. SQL server 2000 does not show the entire hex string to you in the query analyzer if it is greater than a certain length. Thus my hex strings are actually different.

Image.FromStream throws ArgumentException: Parameter is not valid

I'm trying to output an image to the output stream through an HttpHandler but it keeps throwing an ArgumentException. I've googled the issue and tried so many things but I still couldn't fix the problem. Anyway, here's the code:
public void ProcessRequest(HttpContext context)
{
Int32 imageId = context.Request.QueryString["id"] != null ?
Convert.ToInt32(context.Request.QueryString["id"]) : default(Int32);
if (imageId != 0)
{
//context.Response.ContentType = "image/jpeg";
Byte[] imageData = this._imageInfoManager.GetTradeMarkImage(imageId);
using (MemoryStream ms = new MemoryStream(imageData, 0, imageData.Length))
{
using (Image image = Image.FromStream(ms, true, true)) //this line throws
{
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
}
throw new ArgumentException("Image could not be found.");
}
Note that the imageData byte array is not empty and the memory stream is being filled up correctly. Any thoughts?
UPDATE:
Here's the code for GetTradeMarkImage... Note that the images are stored in the an SQL Server database in the image format.
public Byte[] GetTradeMarkImage(Int32 id)
{
object result = DB.ExecuteScalar(SqlConstants.SqlProcedures.Request_GetImageById, id);
if (result != null)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, result);
return ms.ToArray();
}
}
return null;
}
Okay, now you've posted the GetTradeMarkImage code, that's almost certainly the problem:
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, result);
return ms.ToArray();
}
Why would you expect the value of BinaryFormatter to be a valid image stream? It's not clear what's in your database (a BLOB?) nor what the execution-time type of result is here (which you should find out by debugging) but you shouldn't be using BinaryFormatter here. I suspect you just want to get the raw data out of the database, and put that into the byte array.
If you're lucky, you may just be able to cast result to byte[] to start with. (I don't know what ExecuteScalar does with blobs, and this obviously isn't the "normal" ExecuteScalar method anyway). Otherwise, you may need to use an alternative approach, opening a DataReader and getting the value that way.

Serializing / Deserializing image to / from XElement

I am trying to serialize image into XElement and afterwards deserialize it for further use.
I am using this method to serialize:
public XElement subElement = new XElement("Element");
private void Serialize(System.Windows.Forms.Button button) {
if (button.Image != null) {
var bf = new BinaryFormatter();
var ms = new MemoryStream();
bf.Serialize(ms, button.Image);
var textWriter = new StringWriter();
var writer = new XmlTextWriter(textWriter);
byte[] imageBytes = ms.ToArray();
writer.WriteBase64(imageBytes, 0, imageBytes.Length);
subElement.Add(new XAttribute("Image", imageBytes));
}
}
But I can't figure out how to deserialize. I tried something like this:
private void Deserialize(XElement element) {
if (element.Attribute("Image") != null) {
//tried XmlReader reader = XmlReader.Create(new StringReader(element.Attribute("Image").Value));
//but reader is empty
//when I try: XmlReader reader = XmlReader.Create(element.Attribute("Image").Value);
//exception is thrown because XmlReader expects path, not element
}
}
I basically only need to get byte array from XElement, later I know how to handle it.
Once you have your byte array you can do Convert.ToBase64String(byteArray). The result of that function (a string) is what goes in the XAttribute's value.
Then when it comes to reading, you'd just do byteArray = Convert.FromBase64String(element.Attribute("Image").Value)
This should prevent the issues with saving the string within the XML file.
Don't use serialization, simple save/load it from memory stream.

C# Object Binary Serialization

I want to make a binary serialize of an object and the result to save it in a database.
Person person = new Person();
person.Name = "something";
MemoryStream memorystream = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(memorystream, person);
How can I transform memorystream in a string type to be saved in database, and after this to be able to deserialize the object?
What you're really asking for is a safe way of representing arbitrary binary data as text and then converting it back again. The fact that it stores a serialized object is irrelevant.
The answer is almost to use Base 64 (e.g. Convert.ToBase64String and Convert.FromBase64String). Do not use Encoding.UTF8.GetString or anything similar - your binary data is not encoded text data, and shouldn't be treated as such.
However, does your database not have a data type for binary data? Check for BLOB, IMAGE and BINARY types...
Here's the sample. TData must be marked [Serializable] and all fields type also.
private static TData DeserializeFromString<TData>(string settings)
{
byte[] b = Convert.FromBase64String(settings);
using (var stream = new MemoryStream(b))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return (TData)formatter.Deserialize(stream);
}
}
private static string SerializeToString<TData>(TData settings)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, settings);
stream.Flush();
stream.Position = 0;
return Convert.ToBase64String(stream.ToArray());
}
}
//-------write to database-------------------------
Person person = new Person();
person.name = "Firstnm Lastnm";
MemoryStream memorystream = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(memorystream, person);
byte[] yourBytesToDb = memorystream.ToArray();
//here you write yourBytesToDb to database
//----------read from database---------------------
//here you read from database binary data into yourBytesFromDb
MemoryStream memorystreamd = new MemoryStream(yourBytesFromDb);
BinaryFormatter bfd = new BinaryFormatter();
Person deserializedperson = bfd.Deserialize(memorystreamd) as Person;
I used something like this
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, Person);
memoryStream.Flush();
memoryStream.Position = 0;
string value = Convert.ToBase64String(memoryStream.ToArray());
Basically, don't save the data as string to the database, there are blob fields available to store binary data.
If you really need to have the data as string, you'll need to convert your byte[] to a string using base64 encoding, and to grab the byte[] from a string use decoding.
Have you not looked into converting the memorystream into a base64hex string to be put into the database?
byte[] mStream = memorystream.ToArray();
string sConvertdHex = System.Convert.ToBase64String(mStream)
Then you can dump the contents sConvertdHex to the database. To deserialize it you need to do the reverse
byte[] mData = System.Convert.FromBase64String(...)
then deserialize mData back to your object.

Categories