在C#中的二进制反序列化期间更改类型 [英] Changing types during binary deserialization in C#

查看:221
本文介绍了在C#中的二进制反序列化期间更改类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个公司的解决方案消耗的第三方服务。通信是通过XML消息进行。在我们这边,我们生成要使用的类的基础上,他们提供给我们,并在某些时候,我们序列化了某些这些类型的进入我们供以后使用数据库中的二进制数据的XML架构。



问题出现在该第三方公司已经改变了从一个布尔值,整数类型的领域之一。现在,当我们试图反序列化已经存在,我们可以预见得到一个类型转换的异常数据(不能从布尔值转换为整数)



我的问题是 - 怎么办我们去与老布尔类型将其转换为新的整数类型反序列化在数据库中现有的数据?



我已经尝试了很多东西 - 其中有反射和实施ISerializable的,但到目前为止,什么也没有摇出。理想的解决办法是实现ISerializable的,但我得到尝试反序列化现有的数据,因为它是只使用Serializable属性已经连载时,找不到成员的错误。



任何建议,欢迎



编辑:!添加一些代码来清楚地说明我的问题。

 命名ClassLibrary 
{
[Serializable接口]
公共类Foo //:ISerializable的
{
公共BOOL酒吧{搞定;组; }

公共美孚(){}

// [OnDeserializing()]
//内部空隙OnDeserializingMethod(的StreamingContext上下文)
// {
//酒吧= 10;
//}

//公共美孚(的SerializationInfo信息,的StreamingContext上下文)
// {
//酒吧=(INT)info.GetValue(酒吧的typeof(INT));
//}

//公共无效GetObjectData使用(的SerializationInfo信息,的StreamingContext上下文)
// {
// info.AddValue(酒吧,酒吧) ;
//}
}
}

命名空间ConsoleApplication2
{
静态类节目
{
静态无效主要(字串[] args)
{
富富;

//运行#1,其中Foo.Bar是一个布尔值

富=新的Foo();
foo.Bar = TRUE;
SerializeObject(富);
字节[]数据= File.ReadAllBytes(@\Test.bin。);
富= DeserializeObject(数据)的Foo;

//现在改Foo.Bar为整数类型,注释以上各行,并取消低于
//字节[] = newData的File.ReadAllBytes两行(@。\\ \\Test.bin)​​;
//富= DeserializeObject(newData)为Foo;

Console.WriteLine(foo.Bar);
到Console.ReadLine();
}

私有静态对象DeserializeObject(字节[] BUFF)
{
如果(BUFF == NULL)返回NULL;
的BinaryFormatter格式=新的BinaryFormatter();
formatter.Binder =新CustomSerializationBinder();
的MemoryStream毫秒​​=新的MemoryStream(BUFF);
返回formatter.Deserialize(毫秒);
}

私有静态无效SerializeObject(obj对象)
{
如果(OBJ == NULL)回报;
的BinaryFormatter格式=新的BinaryFormatter();使用
(的FileStream毫秒​​=新的FileStream(@\Test.bin。FileMode.Create))
{
formatter.Serialize(MS,OBJ);
}
}
}


解决方案

您可以处理 ISerializable的这种情况下,但是你需要通过循环使用的 SerializationInfo.GetEnumerator < /一>以确定什么类型的数据实际上是从流读出。此外,你需要注意,的BinaryFormatter 连载的字段的没有的属性的,因此,如果使用类的自动实现的属性的那么以前存储二进制流中的名称将是<一个HREF =htt​​ps://stackoverflow.com/questions/8817070/is-it-possible-to-access-backing-fields-behind-auto-implemented-properties> 支持字段的名称的。的属性而不是名称



例如,假设这是你的原班:

  [Serializable接口] 
公共类Foo
{
公共BOOL酒吧{搞定;组; }

公共美孚(){}
}

现在要更改酒吧为整数。序列化或反序列化新旧的BinaryFormatter 流,请使用以下实施 ISerializable的的:

  [Serializable接口] 
公共类Foo:ISerializable的
{
公众诠释吧{搞定;组; }

公共美孚(){}

公共美孚(的SerializationInfo信息,的StreamingContext上下文)
{
VAR枚举= info.GetEnumerator();
,而(enumerator.MoveNext())
{
无功电流= enumerator.Current;
的Debug.WriteLine(的String.Format({0}类型的{1}:{2},current.Name,current.ObjectType,current.Value));
如果(current.Name ==酒吧&放大器;&安培; current.ObjectType == typeof运算(INT))
{
酒吧=(INT)current.Value;
}
,否则如果(current.Name ==<酒吧GT; k__BackingField&放大器;&安培; current.ObjectType == typeof运算(布尔))
{
VAR老= (布尔)current.Value;
酒吧=(旧?1:0); // 管他呢。
}
}
}

公共无效GetObjectData使用(的SerializationInfo信息,的StreamingContext上下文)
{
info.AddValue(酒吧,酒吧);
}
}


One of the solutions in our company consumes a 3rd party service. Communication is done through XML messaging. On our end, we generate classes to be used based on XML schemas that they provide us, and at some point we serialize some of those types into a binary blob in our database for later use.

The problem comes in where that 3rd party company has changed one of the fields from a boolean to an integer type. Now when we try to deserialize the data that's already there we predictably get a type conversion exception (can't convert from boolean to integer).

My question is - how do we go about deserializing the existing data in our database with the old boolean type to convert it to the new integer type?

I've tried a number of things - among which were reflection and implementing ISerializable, but nothing's panned out so far. The ideal solution would be to implement ISerializable, but I get a "Member not found" error when trying to deserialize the existing data because it was already serialized using only the Serializable attribute.

Any suggestions are welcome!

Edit: Adding some code to clearly demonstrate my problem.

namespace ClassLibrary
{
    [Serializable]
    public class Foo //: ISerializable
    {
        public bool Bar { get; set; }

        public Foo() { }

        //[OnDeserializing()]
        //internal void OnDeserializingMethod(StreamingContext context)
        //{
        //    Bar = 10;
        //}

        //public Foo(SerializationInfo info, StreamingContext context)
        //{
        //    Bar = (int)info.GetValue("Bar", typeof(int));
        //}

        //public void GetObjectData(SerializationInfo info, StreamingContext context)
        //{
        //    info.AddValue("Bar", Bar);
        //}
    }
}

namespace ConsoleApplication2
{
    static class Program
    {
        static void Main(string[] args)
        {
            Foo foo;

            // Run #1, where Foo.Bar is a boolean

            foo = new Foo();
            foo.Bar = true;
            SerializeObject(foo);
            byte[] data = File.ReadAllBytes(@".\Test.bin");
            foo = DeserializeObject(data) as Foo;

            // Now change Foo.Bar to an integer type, comment the lines above, and uncomment the two lines below
            //byte[] newData = File.ReadAllBytes(@".\Test.bin");
            //foo = DeserializeObject(newData) as Foo;

            Console.WriteLine(foo.Bar);
            Console.ReadLine();
        }

        private static Object DeserializeObject(byte[] buff)
        {
            if (buff == null) return null;
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Binder = new CustomSerializationBinder();
            MemoryStream ms = new MemoryStream(buff);
            return formatter.Deserialize(ms);
        }

        private static void SerializeObject(Object obj)
        {
            if (obj == null) return;
            BinaryFormatter formatter = new BinaryFormatter();
            using (FileStream ms = new FileStream(@".\Test.bin", FileMode.Create))
            {
                formatter.Serialize(ms, obj);
            }
        }
    }

解决方案

You can handle this situation with ISerializable, however you will need to loop through the deserialized properties using SerializationInfo.GetEnumerator to determine what type of data was actually read from the stream. In addition, you need to be aware that BinaryFormatter serializes fields not properties, so if your class used Auto-Implemented Properties then the name previously stored in the binary stream will be the name of the backing field not the name of the property.

For instance, say this is your original class:

[Serializable]
public class Foo
{
    public bool Bar { get; set; }

    public Foo() { }
}

Now you want to change Bar to an integer. To serialize or deserialize both old and new BinaryFormatter streams, use the following implementation of ISerializable:

[Serializable]
public class Foo : ISerializable
{
    public int Bar { get; set; }

    public Foo() { }

    public Foo(SerializationInfo info, StreamingContext context)
    {
        var enumerator = info.GetEnumerator();
        while (enumerator.MoveNext())
        {
            var current = enumerator.Current;
            Debug.WriteLine(string.Format("{0} of type {1}: {2}", current.Name, current.ObjectType, current.Value));
            if (current.Name == "Bar" && current.ObjectType == typeof(int))
            {
                Bar = (int)current.Value;
            }
            else if (current.Name == "<Bar>k__BackingField" && current.ObjectType == typeof(bool))
            {
                var old = (bool)current.Value;
                Bar = (old ? 1 : 0); // Or whatever.
            }
        }
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bar", Bar);
    }
}

这篇关于在C#中的二进制反序列化期间更改类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆