【问题标题】:Skip elements during deserialization with BinaryFormatter在使用 BinaryFormatter 反序列化期间跳过元素
【发布时间】:2014-04-16 09:44:39
【问题描述】:

反序列化时是否有可能跳过序列化流的下一个“条目”?关于面向插件的架构,序列化对象图的不同部分可能会在另一个环境中成为未知类型(假设它们可以被安全地忽略)。尝试反序列化这些当然会失败。

abstract class Thing{}
class OneThing : Thing {}  // <-- known in environment A & B
class SomeThing : Thing {} // <-- only known in environment A
...
var things = new List<Thing>();
...
things.Add(  (OneThing)(formatter.Deserialize(stream)) );
things.Add( (SomeThing)(formatter.Deserialize(stream)) ); // <-- skip in B
things.Add(  (OneThing)(formatter.Deserialize(stream)) );

如何使用二进制格式化程序来实现这一点?我是否必须计算长度并检索序列化条目的明确类型名称(例如作为字符串)并将其存储在条目本身之前,以便在反序列化时跳过它(通过增加流指针)?或者有没有更好的替代方案,对序列化表示的特定操作问题较少?

【问题讨论】:

  • BinarySerializationEngine ?你的意思是BinaryFormatter
  • 是的(感谢您的提示)。我把它改成了BinaryFormatter

标签: c# serialization plugins binary-serialization


【解决方案1】:

BinaryFormatter 是一个基于类型的序列化器。坦率地说,如果类型未知(并且已知是相同,包括程序集/身份),那么首先使用BinaryFormatter 是一个非常糟糕的主意。大多数序列化程序如果无法理解数据就会窒息,而没有真正提供出色的“跳过”选项。我知道 protobuf-net 确实 允许对这种类型的场景进行简单的恢复,但是 protobuf-net 想要提前知道子类型,这并不能使其非常适用于插件场景。

【讨论】:

  • Mhh.. 我真的不想交换二进制格式化程序,只要它是项目许多其他部分的最佳选择(也许我必须重新考虑)。也许另一个想法是将每个插件数据放入自己的序列化文件中,将它们压缩在一起......我会看看 protobuf-net。感谢您的回复。
  • @mvondano 注意:我不建议 protobuf-net 非常适合插件场景;不过,它确实适用于未知的子类型场景
【解决方案2】:

我尝试了通过增加指针来简单地跳过流部分的版本,这起到了作用。目前这对我有用(尽管它可能不是最好的解决方案):

interface ISerializableObject { }

class PluginSerialization
{
    private readonly IFormatter formatter;

    public PluginSerialization(IFormatter f)
    {
        formatter = f;
    }

    public void SerializeToStream(IEnumerable<ISerializableObject> components, Stream s)
    {
        foreach (var component in components)
        {
            using (var cStream = new MemoryStream())
            {
                formatter.Serialize(cStream, component);
                cStream.Flush();

                // write to stream [length] [type as string] [object]
                formatter.Serialize(s, cStream.Length);
                formatter.Serialize(s, component.GetType().ToString());
                cStream.WriteTo(s);
            }
        }
    }

    public List<ISerializableObject> DeserializeFromStream(Stream s, Func<string, bool> isKnownType )
    {
        var components = new List<ISerializableObject>();

        while (s.Position < s.Length - 1)
        {
            var length = (long)(formatter.Deserialize(s));
            var name = (string)(formatter.Deserialize(s));

            // skip unknown types
            if (!isKnownType(name))
            {
                s.Position += length;
                continue;
            }

            components.Add((ISerializableObject) (formatter.Deserialize(s)));                
        }

        return components;
    }
}

这允许对不同对象列表的部分反序列化 (List&lt;ISerializableObject&gt;())。但是,存储数据的方式和顺序(长度、类型名称、对象)是一个特定的实现细节,因此应该尽可能地进行封装。

【讨论】:

    【解决方案3】:

    我遇到了类似的问题,我想我找到了解决方案。

            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                BinaryFormatter bfmt = new BinaryFormatter()
                {
                    SurrogateSelector = new NonSerializableSurrogateSelector(),
                    Binder = new IgnoreUnknownTypesBinder()
                };
    
                dict1 = (Dictionary<string, object>)bfmt.Deserialize(fs);
                dict2 = (Dictionary<string, string>)bfmt.Deserialize(fs);
    
            }
    

    和辅助类代码

    /// <summary>
    /// Returns NullSurrogate for all types not marked Serializable
    /// </summary>
    public class NonSerializableSurrogateSelector : ISurrogateSelector
    {
        public void ChainSelector(ISurrogateSelector selector)
        {
            throw new NotImplementedException();
        }
    
        public ISurrogateSelector GetNextSelector()
        {
            throw new NotImplementedException();
        }
    
        public ISerializationSurrogate GetSurrogate(
          Type type, StreamingContext context, out ISurrogateSelector selector)
        {
            if (!type.IsSerializable)
            {
                //type not marked Serializable
                selector = this;
                return new NullSurrogate();
            }
            // use default surrogate
            selector = null;
            return null;
        }
    }
    
    public class NullSurrogate : ISerializationSurrogate
    {
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
        }
    
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context,
                                    ISurrogateSelector selector)
        {
            return null;
        }
    }
    
    public class IgnoreUnknownTypesBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            try
            {
                var assembly = Assembly.Load(assemblyName);
                var type = assembly.GetType(typeName);
                return type;
            }
            catch
            {
                return typeof(IgnoreUnknownTypesBinder); // here could be any non-serializable class
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-10
      相关资源
      最近更新 更多