通用二进制读取器

时间:2012-03-27 03:49:27

标签: c# .net binary deserialization

我正在编写二进制序列化器/ deserialser来将许多对象类型转换为字节流。对象表示通过蓝牙或USB连接的设备的API命令及其相关响应。我正在使用BinaryWriter& BinaryReader在流中写入/读取。

序列化器很简单。要序列化的特性标记有一个属性,该属性指定将它们写入字节流的顺序。我使用反射和重载决策句柄迭代属性,选择Write(...)的正确BinaryWriter方法。

deserialiser并不那么简单。我再次可以遍历特定响应类中的特性,我期望确定需要从流中读取的类型。棘手的一点是选择正确的方法来调用BinaryReader来读取我需要的值。我想到了两种方法。

  1. 一个大的switch语句,它根据要读取的类型调用正确的ReadXXXX()方法。
  2. 使用我需要的类型名称来构建字符串中我需要的方法的名称,然后使用relection调用该方法。
  3. 我有没有想到更简单的方法?根据您想要的返回类型,您无法进行重载解析,这简直太可惜了。

2 个答案:

答案 0 :(得分:1)

我在二进制解串器中使用了选项1(大开关语句)。一个更干净的方法可能是:

{
    object result;

    BinaryReader ...;
    foreach (var propertyInfo in ...)
    {
        Func<BinaryReader, object> deserializer;
        if (!supportedTypes.TryGetValue(propertyInfo.PropertyType, out deserializer))
        {
            throw new NotSupportedException(string.Format(
                "Type of property '{0}' isn't supported ({1}).", propertyInfo.Name, propertyInfo.PropertyType));
        }

        var deserialized = deserializer(reader);
        propertyInfo.SetValue(result, deserialized, null);
    }
}

private static Dictionary<Type, Func<BinaryReader, object>> supportedTypes = new Dictionary<Type, Func<BinaryReader, object>>
{
    { typeof(int), br => br.ReadInt32() },
    // etc
};

另一种选择是让命令类本身进行序列化:

interface IBinarySerializable
{
    void Serialize(BinaryWriter toStream);
    void Deserialize(BinaryReader fromStream);
}

然后在你的命令中:

abstract class Command : IBinarySerializable
{

}

class SomeCommand : Command
{
    public int Arg1 { get; set; }

    public void Serialize(BinaryWriter toStream)
    {
        toStream.Write(Arg1);
    }

    public void Deserialize(BinaryReader fromStream)
    {
        Arg1 = fromStream.ReadInt32();
    }
}

通用序列化方法:

void Serialize<T>(T obj) where T : IBinarySerializable
{
    obj.Serialize(_stream);
}

T Deserialize<T>() where T : new(), IBinarySerializable
{
    var result = new T();
    result.Deserialize(_stream);
    return result;
}

但是这样你最终可能会复制一些代码。 (另一方面,派生类可以调用它们的父类类的Serialize / Deserialize,如果在你的场景中有意义的话,这很有效。)

答案 1 :(得分:0)

我不知道我是否完全明白你要做什么。但是听起来你应该仔细看看BinaryFormatter及其在.NET中的序列化代理。 BinaryFormatter允许您轻松地序列化和反序列化对象,序列化代理允许您添加自定义序列化和反序列化逻辑,并且还可以将一个对象重新映射到另一个对象。

看看这里:

的BinaryFormatter http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx

ISerializationSurrogate http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializationsurrogate.aspx

SurrogateSelector http://msdn.microsoft.com/en-us/library/system.runtime.serialization.surrogateselector.aspx

SerializationBinder http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder.aspx

你还可以在这里看到一个小例子,其中我有一个方法可以将任何对象序列化为base64编码的字符串,然后是一个可以反序列化该字符串的反序列化方法。我还将一个SerializationBinder添加到格式化程序中,该格式化程序将MyOldClass类型的序列化重新映射到MyNewClass类型,并添加一个自定义ISerializationSurrogate,它可以在将对象添加到ner类之前处理对象中字段的值。

public class SerializeDeserializeExample {
   public string Serialize(object objectToSerialize) {
      using(var stream = new MemoryStream()) {
         new BinaryFormatter().Serialize(stream, objectToSerialize);
         return Convert.ToBase64String(stream.ToArray());
      }
   }

   public object Deserialize(string base64String) {
      using(var stream = new MemoryStream(Convert.FromBase64String(base64String))) {
         var formatter = new BinaryFormatter();
         var surrogateSelector = new SurrogateSelector();
         formatter.SurrogateSelector = surrogateSelector;
         formatter.Binder = new DeserializationBinder(surrogateSelector);
         return formatter.Deserialize(stream);
      }
   }
}


public class MyDeserializationBinder : SerializationBinder {
   private readonly SurrogateSelector surrogateSelector;

   public MyDeserializationBinder(SurrogateSelector surrogateSelector) {
      this.surrogateSelector = surrogateSelector;
   }

   public override Type BindToType(string assemblyName, string typeName) {
      if(typeName.Equals("MyOldClass", StringComparison.InvariantCultureIgnoreCase)) {
         return RemapToType();
      }
      return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
   }

   private Type RemapToType() {
      var remapToType = typeof(MyNewClass);
      surrogateSelector.AddSurrogate(remapToType,
                              new StreamingContext(StreamingContextStates.All),
                              new MyCustomDeserializationSurrogate());
      return remapToType;
   }
}

public sealed class MyCustomDeserializationSurrogate : ISerializationSurrogate {

   public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) {
      throw new NotImplementedException();
   }

   public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
      var objectType = obj.GetType();
      var fields = GetFields(objectType);
      foreach(var fieldInfo in fields) {
         var fieldValue = info.GetValue(fieldInfo.Name, fieldInfo.FieldType);
         fieldValue = DoSomeProcessing(fieldValue);
         fieldInfo.SetValue(obj, fieldValue);
      }
      return obj;
   }

   private static IEnumerable<FieldInfo> GetFields(Type objectType) {
      return objectType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly |
                           BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
   }

   private static object DoSomeProcessing(object value){
      //Do some processing with the object
   }
}