2016-09-09 5 views
0

Я хотел бы преобразовать переменную анонимного типа в byte [], как я могу это сделать?Сериализация анонимных типов

Что я пробовал:

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

Я получил сообщение об ошибке:

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,

Может кто-нибудь мне помочь? Что я делаю неправильно? Или это невозможно сделать?

+2

Что вы делаете неправильно, вы ожидаете, что анонимные типы будут сериализованы с BinaryFormatter, в основном. Как говорит ошибка, тип не помечен как сериализуемый, что меня не особенно удивляет. Для анонимного типа для поддержки сериализации будет значительная головная боль, и в любом случае она не будет работать для несериализуемых типов свойств. –

+0

Вы считали, что используете реальный тип и помечены как сериализуемые? –

+0

Анонимные типы были добавлены для облегчения поддержки LINQ, чтобы избежать необходимости создавать именованные типы для всего. Они не предназначены для долговременного сохранения, транспортировки или даже для прохождения внутри вашей программы. Они предназначены для использования на местном уровне. Таким образом, анонимные типы не помечены как сериализуемые.Поскольку использование двоичной сериализации означает, что вы позже захотите их десериализовать, было бы лучше, если бы вы сказали нам, что вы хотите достичь с точки зрения функциональности, а затем, возможно, люди могут придумать лучший ответ, чем «извините, t сделать ». –

ответ

1

Необходимо, чтобы типы, которые должны быть сериализованы с семейством сериализаторов по умолчанию (XmlSerializer, BinaryFormatter, DataContractSerializer, ...), должны быть помечены как [Serializable], должны быть общедоступными и требуют публичных свойств для чтения и записи.

Анонимные типы не выполняют эту роль, поскольку они не обладают ни одним из необходимых свойств.

Просто создайте тип, который это делает, и сериализуйте это вместо этого.

1

Просто создайте сериализуемая класс

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

И вы можете сериализовать ваш объект следующим образом:

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

Но I'ts не возможно сериализовать анонимный тип

+0

Спасибо всем, я буду использовать свой пользовательский класс Serializable – user3018896

+0

@ user3018896 отлично, добро пожаловать – fubo

3

Во-первых: правильный подход, как указывали другие, создать подходящий класс для сериализации.

Однако на самом деле можно сериализовать анонимный объект с помощью Json.Net. Заметьте, что я не рекомендую делать это в реальном проекте - это только любопытство.

Этот код полагается на подлый способ доступа к основной тип анонимного объекта с помощью объекта в качестве примерной держатель типа:

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); 
     } 
    } 
} 
+0

интересный ответ, не знаете, как вы знаете, какие члены анонимного типа должны иметь без контракта, но там вы идете. – Jodrell

+0

@Jodrell Действительно, вы можете легко ошибиться, сделав анонимные типы разными в двух местах, которые они используют, что является одной из многих причин, почему этот подход не должен использоваться в реальном коде. –

0

Вы можете, но только если вы сошли с ума. Не используйте это. Это была более забавная проблема.

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

Как и в предыдущих ответах, я использую JSON.NET hack;

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

Я использую динамические объекты для моих целей ведения журнала, и это работает очень хорошо, так как для этого мне не нужна схема.