BinaryFormatter - 是否可以在没有程序集的情况下反序列化已知类?

时间:2012-11-27 23:05:47

标签: c# deserialization binaryformatter

我目前正在尝试与首先使用C#的BinaryFormatter格式化数据后通过网络发送数据的程序进行互操作。这是一个愚蠢的想法,我讨厌它,但我必须与之互操作。

我知道这种类型是什么样的,我知道它的确切布局。但由于各种原因,我无法在程序中添加对该特定程序集的引用。

考虑到BinaryFormatter与特定类型/版本的紧密耦合,尽管知道了数据结构,但我似乎无法找到一种方法来反序列化。

我目前正在考虑创建一个具有所有正确属性的虚假程序集并尝试将其链接(似乎非常混乱),或手动尝试通过二进制流并提取我正在寻找的值(我正在查看有关这方面的MS文档,并且它显然是布局的泥浆。

其他任何伟大的想法,或者过去曾有人取得过成功吗?好像我知道我需要的所有信息,并且BinaryFormatter对此非常脆弱。

编辑:

要回答下面的问题(这是一个好点,顺便说一句),有几个原因。

  1. 项目清洁度。为一个功能外部的.exe添加一个5MB的引用有点过时了。

  2. 我正在与之互操作的各种设备在世界各地部署了各种版本。我关心的项目的内部数据结构在所有项目中是相同的,但程序集的版本不同,导致BinaryFormatter破坏。我可以将二进制流转换为字符串,搜索版本号然后加载正确的版本,但现在我有十几个.exe正在等待我加载正确的版本。那个方案不是很有前途的证明(好吧,整个方案并不是真正的未来证明,但我至少想要抽象出BinaryFormatter的一些脆弱性,以使我的工作更轻松)。只是编写这个响应让我想到使用emit或类似的东西来动态创建一个自定义程序集.....但是男人,必须有一个更简单的方法,对吧?我实际上是在寻找中等大小数据结构中的几个bool。

  3. 对象的变量通过get / set属性公开,这些属性对它们有一些逻辑,并尝试进行函数调用并更新可能不存在于我身边的其他对象(也就是说,get得到了我的值我需要,但也会触发通过链接依赖项的通知,我可以将异常冒泡到我的应用程序。谈论代码味道!)。它变成了依赖/实现兔子洞。

  4. edit2:制造商正在与我合作以使他们的系统更好,但是当我们宣传“与X一起工作”时,我们希望它只使用X,而不需要特定的版本。特别是我们的一些客户系统受到严格的版本控制,只是更新有问题的应用程序成为一项重大工作。

2 个答案:

答案 0 :(得分:1)

正如您在评论中推测的那样,SerializationSurrogate和SerializationBinder可以帮助您解决问题。鉴于您只对几个属性感兴趣,您可以反序列化为代理类,您可以通过枚举传递给SerializationSurrogate的SerializationInfo来填充代理类。不幸的是,它只会让你成为那里的一部分。

问题是访问属性不会触发序列化对象中的任何副作用,因为您只使用SerializationSurrogate访问数据 - 没有任何二进制代码被执行。快速&下面的脏测试代码说明了问题:

namespace BinaryProxy
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;

    [Serializable]
    class TestClass
    {


        public bool mvalue;

        public TestClass(bool value)
        {
            BoolValue = value;
        }

        public bool BoolValue
        {
            get
            {
                // won't happen
                SideEffect = DateTime.Now.ToString();
                return mvalue;
            }

            set
            {
                mvalue = value;
            }
        }

        public string SideEffect { get; set; }

    }

    class ProxyTestClass
    {
        private Dictionary<string, object> data = new Dictionary<string, object>();

        public Object GetData(string name)
        {
            if(data.ContainsKey(name))
            {
                return data[name];
            }
            return null;
        }
        public void SetData(string name, object value)
        {
            data[name] = value;
        }

        public IEnumerable<KeyValuePair<string, object>> Dump()
        {
            return data;
        }
    }

    class SurrogateTestClassConstructor : ISerializationSurrogate
    {
        private ProxyTestClass mProxy;
        /// <summary>
        /// Populates the provided <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the object.
        /// </summary>
        /// <param name="obj">The object to serialize. </param>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param>
        /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Populates the object using the information in the <see cref="T:System.Runtime.Serialization.SerializationInfo"/>.
        /// </summary>
        /// <returns>
        /// The populated deserialized object.
        /// </returns>
        /// <param name="obj">The object to populate. </param>
        /// <param name="info">The information to populate the object. </param>
        /// <param name="context">The source from which the object is deserialized. </param>
        /// <param name="selector">The surrogate selector where the search for a compatible surrogate begins. </param>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
        {
            if (mProxy == null) mProxy = new ProxyTestClass();
            var en = info.GetEnumerator();
            while (en.MoveNext())
            {
                mProxy.SetData(en.Current.Name, en.Current.Value);


            }
            return mProxy;

        }



    }

    sealed class DeserializeBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {


            return typeof(ProxyTestClass);
        }
    }

    static class Program
    {

        static void Main()
        {
            var tc = new TestClass(true);
            byte[] serialized;
            using (var fs = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(fs, tc);
                serialized = fs.ToArray();

                var surrSel = new SurrogateSelector();
                surrSel.AddSurrogate(typeof(ProxyTestClass),
                    new StreamingContext(StreamingContextStates.All), new SurrogateTestClassConstructor());

                using (var fs2 = new MemoryStream(serialized))
                {
                    var formatter2 = new BinaryFormatter();
                    formatter2.Binder = new DeserializeBinder();
                    formatter2.SurrogateSelector = surrSel;
                    var deser = formatter2.Deserialize(fs2) as ProxyTestClass;
                    foreach (var c in deser.Dump())
                    {
                        Console.WriteLine("{0} = {1}", c.Key, c.Value);
                    }
                }

            }

        }
    }
}

在上面的示例中,TestClass'SideEffect支持字段将保持为空。

如果您不需要副作用并且只想访问某些字段的值,则该方法有些可行。除非遵循Jon Skeet的建议,否则我无法想到任何其他可行的解决方案。

答案 1 :(得分:0)

您是否可以添加 能够添加对该特定程序集的引用的额外图层(或单独的服务)?如果是这样,您可以使该小层反序列化二进制格式并将其重新格式化为更可移植的(JSON,协议缓冲区,XML,任何适当的)。把它想象成BinaryFormatter:)

的疯狂隔离层