Serializing anonymous types

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-23 16:34:14

问题


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?


回答1:


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:


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:


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




回答4:


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;
            }
        }
    }
}



回答5:


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.



来源:https://stackoverflow.com/questions/39406505/serializing-anonymous-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!