Google Protocol Buffers - protobuf-net deserialization not working properly - c#

I am experiencing difficulties successfully deserializing a byte array that had been serialized with a pre-compiled protobuff serializer class ModelSerializer. Using this, the serialization and deserialization of classes within my defined data model MyData works when I serialize to a file and deserialize from the file to MyData.
However, I have another requirement, which is to serialize MyData to a byte array, and deserialize the byte array to MyData. Below is the basic class called MyDataConverter that has 2 static methods, one to convert MyData to the byte array and the other to convert a byte array to MyData.
I could serialize MyData to a byte array successfully and write it onto a MemoryStream. However, when I deserialize the byte array, I do get a non-null instance of MyData, but all the data in it is lost (zero default, or null values for custom types).
Could I have gone wrong here? Note that I did use similar code to successfully serialize to a file and deserialize from a file, so it probably isnt something wrong with MyModel nor the pre compiled ModelSerializer. But I could paste some of that info here if necessary.
public class MyDataConverter
{
public static byte [] MyDataToBytes (MyData myData)
{
MemoryStream stream = new MemoryStream();
ModelSerializer serializer = new ModelSerializer();
serializer.Serialize (stream, myData);
byte [] bytes = stream.ToArray();
Stream.Close();
return bytes;
}
public static MyData BytesToMyData (byte [] bytes)
{
MyData myData = null;
MemoryStream stream = new MemoryStream();
stream.Write (bytes, 0, bytes.Length);
ModelSerializer serializer = new ModelSerializer();
myData = (MyData) serializer.Deserialize (stream, myData, typeof (MyData));
stream.Close();
return myData;
}
}

This is the problem:
MemoryStream stream = new MemoryStream();
stream.Write (bytes, 0, bytes.Length);
ModelSerializer serializer = new ModelSerializer();
myData = (MyData) serializer.Deserialize (stream, myData, typeof (MyData));
When you try to deserialize, you're still at the end of the stream. You could just seek to the beginning afterwards, but it would be simpler to change the code to pass the byte array to the constructor:
MemoryStream stream = new MemoryStream(bytes, false);
ModelSerializer serializer = new ModelSerializer();
myData = (MyData) serializer.Deserialize (stream, myData, typeof (MyData));

Can we pass a list of object like List as an input to serialiser.

Related

Correct way to serialise and deserialise data?

I have an array of class data which I'm serialising into a byte array then pushing it into a database. This program runs on a scheduled basis during the night. On the other end I have another program which pulls this data out of the database, processes it into a report - or at least that's the plan.
The class is incased in 2 namespaces, first the the application name, the second is just something to hold my structures. Eg below.
namespace FibreTrend
{
namespace Structures
{
[Serializable]
public class Trend
{
public Trend(DateTime date, string ref, int port)
{
Date = date;
Reference = ref;
PortNo = port;
}
public DateTime Date;
public string Reference;
public int PortNo;
}
}
}
{
// Function to take the trendData list, convert it to a byte array
// List<Structures.Trend> trendData;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream mStream = new MemoryStream())
{
bf.Serialize(mStream, trendData.ToArray());
byte[] b = mStream.ToArray();
// code that pushes the array into the database...
}
}
I have a completely separate application which reads in the data from the database as the byte array. I then go to converting it from the bytes back to my data class.
using (MemoryStream mStream = new MemoryStream())
{
BinaryFormatter binaryFormat = new BinaryFormatter();
mStream.Write(data, 0, data.Length);
mStream.Seek(0, SeekOrigin.Begin);
Structures.Trend[] obj = (Structures.Trend[])binaryFormat.Deserialize(mStream);
}
And here is my error. Its telling me it wants the FibreTrend binary to deserialize the data. Why?? My Trend class is the same size, same data layout, its an exact copy and paste from my other project. Why is it insisting on needing my other binary file in companion. When I do put the binary with it then deserialise it into an object it comes put as a FibreTrend.Structures.Trend[]. I'm obviously not going to include the other binary file with it, I'm also not going to double handle the data converting it to a Report.Structures.Trend[]. Its just a stream of 1s and 0s, why can't I just push it into any class that I deem I want, isn't that the purpose of the cast to tell the compiler how I want the data ordered and structured?
Binary Serialized data stream contains a header with type information in it. You can refer to the Binary Format Data structure here. That's why you are getting that exception about missing assembly.
One way to solve your issue is by implementing a SerializationBinder that overrides the type to be deserialized into at runtime and set Binder property on BinaryFormatter. Here is a very good example.
A preferred solution would be to use alternative serialization formats such as XML, JSON.

Serialize an object in C# and get byte stream

I have an object, instance of a Serializable class. I was wondering how can you get this object as a byte stream?
I know I can use BinaryFormatter and then use the Serialize method, but this method takes a serializationStream where it writes the serialized object. I want to be able to write it in a file/stream in a specific position so I would like to do something like:
obj = new Something(); // obj is serializable
byte[] serialized = obj.serialize(); [*]
file.write(position, serialized)
Is there any way I can do the [*], to take the bytes of the serialization of an object?
MemoryStream m = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(m, new MyClass() {Name="SO"});
byte[] buf = m.ToArray(); //or File.WriteAllBytes(filename, m.ToArray())
[Serializable]
public class MyClass
{
public string Name;
}

Binary deserialization: get object data

Is it possible to get data of the binary serialized object ( or list of othe same objects ) as it can be done in XML or soap. Please note, I have no idea about object structure ( private and public fields,etc)? By data of the binary serialized object I mean the values of all fields.
Lets say you have a stream.
object yourData;
var SerializeBinaryFileName = #"C:\Temp\binary.bf";
using (Stream stream = File.Open(SerializeBinaryFileName, FileMode.Open))
{
BinaryFormatter bformatter = new BinaryFormatter();
yourData = bformatter.Deserialize(stream);
stream.Close();
}
Then you have your object graph in the yourData variable.
You can read it as any other object graph can be read.

Getting error deserializing with DataContractSerializer

When I am deserializing my object back to it's original type my object is always null.
Here is my code:
ProjectSetup obj = new ProjectSetup();
if (System.Web.HttpContext.Current.Session["ProjectSetup"] == null)
setBookProjectSetup();
string toDeserialise = System.Web.HttpContext.Current.
Session["ProjectSetup"].ToString();
DataContractSerializer dcs = new DataContractSerializer(typeof(ProjectSetup));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(toDeserialise));
obj = (ProjectSetup) dcs.ReadObject(ms, true);
return obj;
I'm going to assume that the call to setBookProjectSetup places an instance of ProjectSetup in the HttpSessionState with a key of ProjectSetup.
The issue here starts with this:
string toDeserialise = System.Web.HttpContext.Current.
Session["ProjectSetup"].ToString();
You subsequently use the contents of the toDeserialize string as the source of the deserialization.
Unless you've overloaded ToString to return a byte stream that the DataContractSerializer would be able to deserialize (it's highly unlikely) chances are you are using the implementation of ToString on Object, which will just return the type's name.
Then, you are trying to deserialize that string into your object, which isn't going to work.
What you need to do is properly serialize your object into a byte array/MemoryStream, like so:
using (var ms = new MemoryStream())
{
// Create the serializer.
var dcs = new DataContractSerializer(typeof(ProjectSetup));
// Serialize to the stream.
dcs.WriteObject(ms, System.Web.HttpContext.Current.Session["ProjectSetup"]);
At this point, the MemoryStream will be populated with a series of bytes representing your serialized object. You can then get the object back using the same MemoryStream:
// Reset the position of the stream so the read occurs in the right place.
ms.Position = 0;
// Read the object.
var obj = (ProjectSetup) dcs.ReadObject(ms);
}

C# Deserialization Over TCP

I have now an issue with deserializing an object sent over TCP.
When deserializing, I get the following SerializationException (code below):
Additional information: Binary stream '0' does not contain a valid
BinaryHeader. Possible causes are invalid stream or object version
change between serialization and deserialization.
Serialization code:
public static void SerializeRO(Stream stream, ReplicableObject ro) {
MemoryStream serializedObjectStream = new MemoryStream();
Formatter.Serialize(serializedObjectStream, ro);
BinaryWriter bw = new BinaryWriter(stream);
bw.Write(serializedObjectStream.Length);
serializedObjectStream.WriteTo(stream);
serializedObjectStream.Close();
bw.Close();
}
Deserialization code:
public static List<ReplicableObject> ParseStreamForObjects(Stream stream) {
List<ReplicableObject> result = new List<ReplicableObject>();
while (true) {
if (!(stream as NetworkStream).DataAvailable) break;
BinaryReader br = new BinaryReader(stream);
int length = br.ReadInt32();
byte[] bytes = br.ReadBytes(length);
MemoryStream ms = new MemoryStream(bytes);
ms.Position = 0;
// ERROR OCCURS ON THE LINE BELOW
result.Add((ReplicableObject) Formatter.Deserialize(ms));
ms.Close();
br.Close();
}
return result;
}
The objects are being serialized at runtime, so I don't think it's a versioning issue. I am new to streaming etc, so I may have missed something obvious.
I'd like to suggest what I think it could be, but I'm really stuck. :)
Thanks.
serializedObjectStream.Length is a long.
You're writing a 64-bit value to the network, but you're trying to read it as a 32-bit int.

Categories