0

De-serialization not working. It gives me the following run-time error:

Unhandled Exception: System.InvalidCastException: Unable to cast object of 'Measurement' to type 'Measurement'.

I really can't see what is wrong with it.

//start alternate serialization
public static class AltSerialization
{
    public static byte[] AltSerialize(Measurement m)
    {
     using (var ms = new MemoryStream())
        {
            var bf = new BinaryFormatter();
            bf.Serialize(ms, m);
            return ms.GetBuffer();
        }
    }

    public static Measurement AltDeSerialize(byte[] seriM)    
    {
    using (var stream = new MemoryStream( seriM ))
        {
            BinaryFormatter bf = new BinaryFormatter();
            return (Measurement)bf.Deserialize(stream);         
        }
    } 
}
//end alternte serialization

[Serializable] //This attribute sets class to be serialized
public class Measurement : ISerializable
{            
    [NonSerialized] public int id;
    public int time; //timestamp
    public double value;

    public Measurement()
    {
        id = 1;
        time = 12;
        value = 0.01;
    }

    public Measurement(int _id, int _time, double _value)
    {
        id = _id;
        time = _time;
        value = _value;
    }

    //Deserialization constructor   
    public Measurement(SerializationInfo info, StreamingContext ctxt)
    {
        //Assign the values from info to the approporiate properties    
        Console.WriteLine("DeSerialization construtor called.");
        time = (int)info.GetValue("MeasurementTime", typeof(int));
        value = (double)info.GetValue("MeasurementValue", typeof(double));
    }

    //Serialization function    
    public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
    {
        // Custom name-value pair
        // Values must be read with the same name they're written       
        info.AddValue("MeasurementTime", time);
        info.AddValue("MeasurementValue", value);
    }
}


//AFTER THIS, IS FOR TEST FILES app1.cs, app2.cs, and the reference refer.cs.

//app1.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using refer;
using System.Reflection;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
[assembly: AssemblyVersion("1.0.0.0")]



public class MainClass
{
    public static void Main()
    {
        //Create a new Measurement message
        Measurement m1 = new Measurement(2, 2345, 23.456);
        System.Console.WriteLine("\nm1.id = {0}", m1.id);
        System.Console.WriteLine("m1.time = {0}", m1.time);
        System.Console.WriteLine("m1.value = {0}", m1.value);

        /*byte[] bArray = AltSerialization.AltSerialize( m1 );
        Measurement m2 = new Measurement();
        m2 = AltSerialization.AltDeSerialize(bArray);
        System.Console.WriteLine("\nm2.id = {0}", m2.id);
        System.Console.WriteLine("m2.time = {0}", m2.time);
        System.Console.WriteLine("m2.value = {0}", m2.value);*/

        ConnectionFactory factory = new ConnectionFactory();
        factory.HostName = "localhost";
        using (IConnection connection = factory.CreateConnection())
        using (IModel channel = connection.CreateModel())
        {
            channel.QueueDeclare("hello", true, false, false, null);

            byte[] body = refer.AltSerialization.AltSerialize( m1 );

            channel.BasicPublish("", "hello", null, body);
            Console.WriteLine(" [x] Sent ");
        }
    }
}


//app2.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using refer;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Reflection;
[assembly: AssemblyVersion("1.0.0.0")]



public class MainClass
{
    public static void Main()
    {

        /*/Create a new Measurement message
        Measurement m1 = new Measurement(2, 2345, 23.456);
        System.Console.WriteLine("\nm1.id = {0}", m1.id);
        System.Console.WriteLine("m1.time = {0}", m1.time);
        System.Console.WriteLine("m1.value = {0}", m1.value);

        byte[] bArray = AltSerialization.AltSerialize( m1 );*/

        Measurement m2 = new Measurement();

        ConnectionFactory factory = new ConnectionFactory();
        factory.HostName = "localhost";
        using (IConnection connection = factory.CreateConnection())
        using (IModel channel = connection.CreateModel()) {
            channel.QueueDeclare("hello", false, false, false, null);

            QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
            channel.BasicConsume("hello", true, consumer);

            System.Console.WriteLine(" [*] Waiting for messages." +
                                     "To exit press CTRL+C");

                BasicDeliverEventArgs ea =
                    (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                m2 = refer.AltSerialization.AltDeSerialize(ea.Body); 

                System.Console.WriteLine(" \n[x] Received ");
                System.Console.WriteLine("\nm2.id = {0}", m2.id);
                System.Console.WriteLine("m2.time = {0}", m2.time);
                System.Console.WriteLine("m2.value = {0}", m2.value);
        }
    }
}

//refer.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

using System.Reflection;
[assembly: AssemblyVersion("1.0.0.0")]

namespace refer
{
    //start alternate serialization
    public static class AltSerialization
    {
        public static byte[] AltSerialize(Measurement m)
        {
         using (var ms = new MemoryStream())
            {
                var bf = new BinaryFormatter();
                bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
                bf.Serialize(ms, m);
                return ms.GetBuffer();
            }
        }

        public static Measurement AltDeSerialize(byte[] seriM)    
        {
        using (var stream = new MemoryStream( seriM ))
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
                return (Measurement)bf.Deserialize(stream);         
            }
        } 
    }
    //end alternte serialization

    [Serializable] //This attribute sets class to be serialized
    public class Measurement : ISerializable
    {            
        [NonSerialized] public int id;
        public int time; //timestamp
        public double value;

        public Measurement()
        {
            id = 1;
            time = 12;
            value = 0.01;
        }

        public Measurement(int _id, int _time, double _value)
        {
            id = _id;
            time = _time;
            value = _value;
        }

        //Deserialization constructor   
        public Measurement(SerializationInfo info, StreamingContext ctxt)
        {
            //Assign the values from info to the approporiate properties    
            Console.WriteLine("DeSerialization construtor called.");
            time = (int)info.GetValue("MeasurementTime", typeof(int));
            value = (double)info.GetValue("MeasurementValue", typeof(double));
        }

        //Serialization function    
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            // Custom name-value pair
            // Values must be read with the same name they're written       
            info.AddValue("MeasurementTime", time);
            info.AddValue("MeasurementValue", value);
        }
    }
}
public class MainClass
{
    public static void Main() 
    {

    }
}
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
Demi
  • 318
  • 1
  • 4
  • 13
  • did you serialize previously the object you're trying to deserialize now ? – Tigran Jul 11 '11 at 19:42
  • 1
    I have copied your code to my project and tried how it works under .NET 4. Everything is OK. Can you please post the code showing how to repro the issue? – platon Jul 11 '11 at 19:44
  • You have a problem with references in your solution. Remove and re-add all references that contains a Measurement class. Also, look for Measurement classes that exist within different assemblies that you reference. –  Jul 11 '11 at 19:46
  • @Platon: how did you test this serialization issue if you don't have original binaty data files ? You need them, cause the problem is there. – Tigran Jul 11 '11 at 19:46
  • This type of problem is once again why I don't trust `BinaryFormatter` ;p I can happily recommend an alternative binary serializer that is contract-based, so won't suffer this... – Marc Gravell Jul 11 '11 at 19:48
  • @platon If you copied the code and tested it with a Measurement object from one app, it works. But, due to the behavior of my task, one app will do the serialization and another is supposed to interpret (de-serialize) it. Did you tasted it that way? – Demi Jul 11 '11 at 20:05
  • @Will It is just a console .cs file I am compiling and running. I duplicated the code and, I run the first one that just serializes and on another console I run the other duplicate to deserialize it. – Demi Jul 11 '11 at 20:07
  • @Marc Gravell So, how do you do that? – Demi Jul 11 '11 at 20:09
  • @Demi, actually, I have already approved the answer posted by @agent-j. Most likely, he is correct. Your different applications reference difference versions of the assembly where the Measurement type is declared. If this is not the case, please post a test sample. We will make it work. – platon Jul 11 '11 at 20:10
  • @Demi - I wrote my own (protobuf-net), following google's "protobuf" spec; it is faster than `BinaryFormatter`, has smaller output than `BinaryFormatter`, and has much fewer pain points. In particular, google *designed* protobuf to be really easy to "version", so the above scenario simply doesn't happen. We use it here on stackoverflow - AFAIK (and I've looked hard) it is quite simply the fastest serializer available in .NET, and is usually pretty easy to retro-fit onto your existing model. – Marc Gravell Jul 11 '11 at 20:12
  • Important point, btw; you're actually returning an oversized buffer - this will *probably* be OK, but is wasteful. – Marc Gravell Jul 11 '11 at 20:15

5 Answers5

2

Edit:

The assembly names of the console applications are different, so even if the namespace and type names are the same, the BinaryFormatter still records the name of the assembly. Define the Measurement class in a common class library assembly and reference it from both console apps.

Original answer:

Most likely, the side that serialized the object was compiled with a different version of the Assembly than the side that deserialized it. Check in the AssemblyInfo.cs file for the assembly containing Measurement to make sure that the AssemblyVersion is fully specified.

[assembly: AssemblyVersion("1.0.0.0")]

not

[assembly: AssemblyVersion("1.0.*")]

If that doesn't work, make sure that assembly file is identical in both places.

agent-j
  • 27,335
  • 5
  • 52
  • 79
  • Where do I find that for a console program that is compiled from a single .cs file? – Demi Jul 11 '11 at 19:48
  • Add this text at the top of your .cs file: `[assembly: AssemblyVersion("1.0.0.0")]` – agent-j Jul 11 '11 at 19:49
  • I did what you said, and this time it actually goes to the Measurement constructor (that deserializes the byte[]) but still, same error. :/ – Demi Jul 11 '11 at 20:17
  • Are you trying to deserialize data that was serialized using an older version of the app? (If so, you're probably out of luck) – agent-j Jul 11 '11 at 20:25
  • So, I have these two separate console programs (apps) each create their own Measurement object. App 1 serialized the measurement object it just created. On the other end app 2 creates a default measurement object then, de-serialized the byte[] passed to it and is supposed to store the deserialized byte[] into its measurement object. – Demi Jul 11 '11 at 20:38
  • Oh! There's the problem. The assembly names are different, so even if the namespace and type names are the same, the BinaryFormatter still records the name of the assembly. Define the Measurement class in a common class library assembly and reference it from both console apps. – agent-j Jul 11 '11 at 20:42
  • I think so too! Bu how do I do that? :/ – Demi Jul 11 '11 at 21:03
  • If you are working with notepad and the C# compiler, do something like this: `csc /r:consoleapp1.exe consoleapp2.cs`. Otherwise, right click on ConsoleApp2's references in Solution Explorer and click `add reference` select consoleapp1.exe – agent-j Jul 11 '11 at 21:11
  • I'm using notepad and the c# compiler. I have app1.cs app2.cs and created refer.cs (to define the Measurement object). I compiled refer.cs separately and got the .exe file. Then I compiled app1.cs and app2.cs separately adding the reference refer.exe and am running into "WRN: Assembly binding logging is turned off" warning? I copied all the three files here for reference. Can you please take a look at it for me? – Demi Jul 11 '11 at 22:00
  • you should compile refer.cs with `/target:library` so that it becomes a .dll. That is more standard. – agent-j Jul 11 '11 at 22:10
0

You'll get this error if you have two applications, one serializing, and one deserializing and they share a DLL with the serialized type (Measurment) but the shared DLLs are different builds.

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
0

Just trying to guess: you're trying to deserialize binaries with Measurement class which is now in different namespace then in the moment of serialization of that file.

Check the code that previously serialized your data and check that Measurement namepsace in regard of yours.

Regards.

Tigran
  • 61,654
  • 8
  • 86
  • 123
0

Since (comments) you expressed an interest in avoiding this scenario, here's how I would do it:

using System.IO;
using ProtoBuf;
public static class AltSerialization
{
    public static byte[] AltSerialize(Measurement m)
    {
        using (var ms = new MemoryStream())
        {
            Serializer.Serialize(ms, m);
            return ms.ToArray();
        }
    }

    public static Measurement AltDeSerialize(byte[] seriM)
    {
        using (var stream = new MemoryStream(seriM))
        {
            return Serializer.Deserialize<Measurement>(stream);
        }
    }
}

[ProtoContract]
public class Measurement
{
    public int id; // not serialized
    [ProtoMember(1)]
    public int time; // serialized as field 1
    [ProtoMember(2)]
    public double value; // serialized as field 2

    public Measurement()
    {
        id = 1;
        time = 12;
        value = 0.01;
    }

    public Measurement(int _id, int _time, double _value)
    {
        id = _id;
        time = _time;
        value = _value;
    }
}

well, except I wouldn't have public fields ;p If you don't want the attributes, they can be avoided too, in a number of ways that I can explain if you really want.

Advantages:

  • faster and smaller
  • not tied to any platform (you could load that in a C++/java/etc protobuf client very easily)
  • not tied to any particular type or field names
  • no risk of accidentally serializing extra objects through event subscriptions etc
  • most common serialization concepts fully supported
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

One more solution, which might help you. Set the BinaryFormatter's AssemblyFormat property to Simple value in both serialization and deserialization methods:

bf.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;

Does this help?

platon
  • 5,310
  • 1
  • 22
  • 24