5
votes

I'd like to convert anonymous type variable to byte[], how can I do that?

What I tried:

byte[] result;

var my = new
{
    Test = "a1",
    Value = 0
};

BinaryFormatter bf = new BinaryFormatter();

using (MemoryStream ms = new MemoryStream())
{
    bf.Serialize(ms, my); //-- ERROR

    result = ms.ToArray();
}

I've got error:

An exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll but was not handled in user code

Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' in Assembly 'MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.Additional information: Type '<>f__AnonymousType10`2[[System.String, mscorlib,

Can somebody help me? What I'm doing wrong? Or this is not possible to do?

5
What you're "doing" wrong is expecting anonymous types to be serializable with BinaryFormatter, basically. As the error says, the type isn't marked as serializable - which doesn't particularly surprise me. For an anonymous type to support serialization would be a significant headache, and wouldn't work for non-serializable property types anyway.Jon Skeet
Have you considered using a real type, and flagging it as serializable?Rowland Shaw
Anonymous types were added to support LINQ easier, to avoid having to create named types for everything. They are not meant to be used for longterm persistence, transport, or even for passing around inside your program. They are meant to be used locally. As such, anonymous types are not marked as serializable. Since the usage of binary serialization means you later on want to deserialize them, it would be better if you told us what you want to accomplish in terms of functionality, and then perhaps people can come up with a better answer than "sorry, can't be done".Lasse V. Karlsen

5 Answers

4
votes

Just create a serializable class

[Serializable]
class myClass
{
    public string Test { get; set; }
    public int Value { get; set; }
}

And you can serialize your object the following way:

byte[] result;
myClass my = new myClass()
{
    Test = "a1",
    Value = 0
};
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
    bf.Serialize(ms, my); //NO MORE ERROR
    result = ms.ToArray();
}

But i'ts not possible to serialize a anonymous type

3
votes

Types that are to be serialized with the default serializer family (XmlSerializer, BinaryFormatter, DataContractSerializer, ...) need to be marked [Serializable], need to be public types, and will require public read-write properties.

Anonymous types don't fulfill this role, because they have none of the required properties.

Just create a type that does, and serialize that instead.

3
votes

Firstly: the correct approach is, as others have pointed out, to create a proper class for serialization.

However, it is actually possible to serialize an anonymous object using Json.Net. Note that I do not recommend actually doing this in a real project - it's a curiosity only.

This code relies on a sneaky way of accessing the underlying type of an anonymous object by using an exemplar object as a type holder:

using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;

public class Program
{
    static void Main()
    {
        var data = serializeAnonymousObject();
        deserializeAnonymousObject(data);
    }

    static byte[] serializeAnonymousObject()
    {
        // This is in a separate method to demonstrate that you can
        // serialize in one place and deserialize in another.

        var my = new
        {
            Test  = "a1",
            Value = 12345
        };

        return Serialize(my);
    }

    static void deserializeAnonymousObject(byte[] data)
    {
        // This is in a separate method to demonstrate that you can
        // serialize in one place and deserialize in another.

        var deserialized = new  // Used as a type holder
        {
            Test  = "",
            Value = 0
        };

        deserialized = Deserialize(deserialized, data);

        Console.WriteLine(deserialized.Test);
        Console.WriteLine(deserialized.Value);
    }

    public static byte[] Serialize(object obj)
    {
        using (var ms     = new MemoryStream())
        using (var writer = new BsonWriter(ms))
        {
            new JsonSerializer().Serialize(writer, obj);
            return ms.ToArray();
        }
    }

    public static T Deserialize<T>(T typeHolder, byte[] data)
    {
        using (var ms     = new MemoryStream(data))
        using (var reader = new BsonReader(ms))
        {
            return new JsonSerializer().Deserialize<T>(reader);
        }
    }
}
2
votes

You can but only if you are insane. Don't use this. This was more a fun problem.

class Program
    {
        static void Main(string[] args)
        {
            var obj1 = new
            {
                Test = "a1",
                SubObject = new
                {
                    Id = 1
                },
                SubArray = new[] { new { Id = 1 }, new { Id = 2 } },
                Value = 0
            };

            var my = new AnonymousSerializer(obj1);
            BinaryFormatter bf = new BinaryFormatter();

            byte[] data;
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, my);
                ms.Close();
                data = ms.ToArray();
            }

            using (MemoryStream ms = new MemoryStream(data))
            {
                var a = bf.Deserialize(ms) as AnonymousSerializer;

                var obj2 = a.GetValue(obj1);

                Console.WriteLine(obj1 == obj2);

            }
            Console.ReadLine();
        }

        [Serializable]
        public class AnonymousSerializer : ISerializable
        {
            private object[] properties;

            public AnonymousSerializer(object objectToSerializer)
            {
                Type type = objectToSerializer.GetType();
                properties = type.GetProperties().Select(p =>
                {
                    if (p.PropertyType.IsArray && IsAnonymousType(p.PropertyType.GetElementType()))
                    {
                        var value = p.GetValue(objectToSerializer) as IEnumerable;
                        return value.Cast<object>().Select(obj => new AnonymousSerializer(obj)).ToArray() ;
                    }else if (IsAnonymousType(p.PropertyType))
                    {
                        var value = p.GetValue(objectToSerializer);
                        return new AnonymousSerializer(value);
                    }else{
                        return p.GetValue(objectToSerializer);
                    }
                }).ToArray();
            }

            public AnonymousSerializer(SerializationInfo info, StreamingContext context)
            {
                properties = info.GetValue("properties", typeof(object[])) as object[];
            }


            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("properties", properties);
            }

            public T GetValue<T>(T prototype)
            {
                return GetValue(typeof(T));
            }

            public dynamic GetValue(Type type)
            {
                Expression<Func<object>> exp = Expression.Lambda<Func<object>>(Creator(type));
                return exp.Compile()();
            }

            private Expression Creator(Type type)
            {
                List<Expression> param = new List<Expression>();

                for (int i = 0; i < type.GetConstructors().First().GetParameters().Length; i++)
                {
                    var cParam = type.GetConstructors().First().GetParameters()[i];
                    if (cParam.ParameterType.IsArray && IsAnonymousType(cParam.ParameterType.GetElementType()))
                    {
                        var items = properties[i] as AnonymousSerializer[];
                        var itemType = cParam.ParameterType.GetElementType();
                        var data = items.Select(aser => aser.Creator(itemType)).ToArray();
                        param.Add(Expression.NewArrayInit(itemType, data));
                    }
                    else if (IsAnonymousType(cParam.ParameterType))
                    {
                        param.Add((properties[i] as AnonymousSerializer).Creator(cParam.ParameterType));
                    }
                    else
                    {
                        param.Add(Expression.Constant(properties[i]));
                    }
                }

                return Expression.New(type.GetConstructors().First(), param);
            }

            private static bool IsAnonymousType(Type type)
            {
                bool hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
                bool nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
                bool isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;

                return isAnonymousType;
            }
        }
    }
}
0
votes

Similar to previous answers, I am using JSON.NET hack;

public static byte[] DynamicToByteArray(object message)
    {
        string serializeObject = JsonConvert.SerializeObject(message);
        byte[] bytes = Encoding.UTF8.GetBytes(serializeObject);
        return bytes;
    }

I am using dynamic objects my logging purposes, and it works very well since I do not need schema for that.