将非序列化的类字节数组 [英] Converting Non-Serializable Classes to a Byte Array

查看:155
本文介绍了将非序列化的类字节数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一种情况,我非常多不同的系统之间同步数据。 (数据本身是类似的,但在不同的系统表中有相当不同的格式。)为了帮助此同步,我有一个数据库表,该表存储对象从每个系统的哈希值与项目密钥和其他相关信息。当对象从任一系统变更散列,我更新其他

我的数据库表看起来是这样的。

  CREATE TABLE [DBO]。[SyncHashes](
    [SyncHashId] [INT] IDENTITY(1,1)NOT NULL,
    [的ObjectName] [为nvarchar(50)NULL,
    [MappingTypeValue] [为nvarchar(25)NULL,
    [MappingDirectionValue] [为nvarchar(25)NULL,
    [SourceSystem] [为nvarchar(50)NULL,
    [SourceKey] [为nvarchar(200)NULL,
    [SourceHash] [为nvarchar(50)NULL,
    [目标系统] [为nvarchar(50)NULL,
    [TargetKey] [为nvarchar(200)NULL,
    [TargetHash] [为nvarchar(50)NULL,
    [UpdateNeededValue] [为nvarchar(最大)空,
    [CreatedOn] [日期时间] NULL,
    [ModifiedOn] [日期时间] NULL,
    [版本] [时间戳] NOT NULL,
    [IsActive] [位] NOT NULL,
PRIMARY KEY CLUSTERED
(
    [SyncHashId] ASC
)WITH(PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON)ON [PRIMARY]
)ON [PRIMARY]
 

到目前为止好。但是......

为了有效地计算哈希值(如 MD5哈希值的(这是我使用))一个对象,你需要能够将其转换为一个的字节数组的。

看来,为了一个对象转换为字节数组,它必须是序列化的。 (至少这是我看过,而且我从.NET收到错误似乎表明这是真的。)

有关的系统之一,我必须让我的所有数据库对象序列化,这样的伟大的能力。获得哈希生成,一切都被同步,与世界是美丽的!

有关其他系统,事情的没有那么大的。我通过了数据库上下文的距离的实体框架4 的(code第一)模型和实体的没有序列号的。

当我尝试使用类似下面,.NET抱怨并抛出一个小脾气来转换为字节 - 所有的,而拒绝给我可爱的小字节数组我这么客气地要求

 的foreach(在context.TableName VAR的DataItem)
{
    VAR的字节数组=(byte []的)DataItem的;
}
 

确定。没问题。

我有我自己,我想可能做的伎俩一个可爱的小扩展方法。

 公共静态的byte [] ObjectToByteArray< T>(这件T OBJ)
{
    如果(OBJ == NULL)
        返回null;
    BinaryFormatter的BF =新的BinaryFormatter();
    MemoryStream的毫秒=新的MemoryStream();

    bf.Serialize(MS,OBJ);
    返回ms.ToArray();
}
 

不过,哦,不!如果对象(实体)是不可序列化,这个程序将引发我一个漂亮的小(而且完全预期的)异常。

所以...我修改程序,并添加一个where子句中的方法定义如下所示。

 公共静态的byte [] ObjectToByteArray< T>(这件T OBJ)其中T:ISerializable的
{
    如果(OBJ == NULL)
        返回null;
    BinaryFormatter的BF =新的BinaryFormatter();
    MemoryStream的毫秒=新的MemoryStream();

    bf.Serialize(MS,OBJ);
    返回ms.ToArray();
}
 

唯一的问题是,现在我又回到了起点,所有我的对象必须是序列化得到一个字节数组。

嗯。不好。

于是,我把一个黑客遍历所有对象的属性,并生成一个字符串再presentation从中我可以建立一个字节数组。这是丑陋和低效有几分那种它做的伎俩。

 公共静态字符串ComputeMD5Hash< T>(这件T输入)
{
    StringBuilder的SB =新的StringBuilder();

    类型t = input.GetType();
    的PropertyInfo []属性= t.GetProperties();

    的foreach(在性能VAR属性)
    {
        sb.Append(property.Name);
        sb.Append(|);
        对象值= property.GetValue(输入,NULL);
        如果(值!= NULL)
        {
            sb.Append(值);
        }
        sb.Append(|);
    }

    返回MD5HashGenerator.GenerateKey(sb.ToString());
}
 

但是......

在所有这一切,是什么我还是很希望能够被有效且正确地创建从对象的类没有被标记为可序列化字节数组。什么是实现这一目标的最佳方式是什么?

感谢你在前进!

解决方案
  

创建一个对象的类没有标记为可序列化的字节数组

您可以使用 protobuf网 V2做到这一点。下载zip然后引用 protobuf网组件。

考虑这个简单的类定义,我们要序列:

 公共类Person
{
    公共字符串名字{获得;组; }
    公共字符串姓{获得;组; }
    公众诠释年龄{获得;组; }
}
 

然后,您可以序列化这是一个字节数组:

 变种人=新的Person {名字=约翰,姓=史密斯,年龄= 30};
VAR模型= ProtoBuf.Meta.TypeModel.Create();
//添加要序列化的所有属性。
//在这种情况下,我们只是遍历类的所有公共属性
//排序按名称,这样的性能是在predictable订单
VAR属性= typeof运算(人).GetProperties()选择(P => p.Name)。.OrderBy(NAME =>名称).ToArray();
model.Add(typeof运算(人),真)。新增(属性);

byte []的字节数;

使用(VAR的MemoryStream =新的MemoryStream())
{
    model.Serialize(MemoryStream的,人);
    字节= memoryStream.GetBuffer();
}
 

在protobuf网串行器将序列化的的速度更快,产生较小的byte []数组比的BinaryFormatter

警告1 这将只(以目前的形式)序列化类,它看起来确定你使用的公共属性。
警告2 这被认为是脆弱,因为增加一个新的属性可能意味着你无法反序列化对象连载与现有 TypeModel

I have a scenario where I am synchronizing data between multiple VERY dissimilar systems. (The data itself is similar but the tables on the different systems have quite different formats.) To assist with this synchronization, I have a database table which stores object hashes from each of the systems along with item keys and other relevant information. When the hash of an object from either system changes, I update the other.

My database table looks something like this.

CREATE TABLE [dbo].[SyncHashes](
    [SyncHashId] [int] IDENTITY(1,1) NOT NULL,
    [ObjectName] [nvarchar](50) NULL,
    [MappingTypeValue] [nvarchar](25) NULL,
    [MappingDirectionValue] [nvarchar](25) NULL,
    [SourceSystem] [nvarchar](50) NULL,
    [SourceKey] [nvarchar](200) NULL,
    [SourceHash] [nvarchar](50) NULL,
    [TargetSystem] [nvarchar](50) NULL,
    [TargetKey] [nvarchar](200) NULL,
    [TargetHash] [nvarchar](50) NULL,
    [UpdateNeededValue] [nvarchar](max) NULL,
    [CreatedOn] [datetime] NULL,
    [ModifiedOn] [datetime] NULL,
    [Version] [timestamp] NOT NULL, 
    [IsActive] [bit] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [SyncHashId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

So far so good. But...

In order to effectively compute a hash (such as an MD5 hash (which is what i am using)) for an object, you need to be able to convert it to a byte array.

And...

It seems that in order to convert an object to a byte array, it must be serializable. (At least that's what I have read, and the errors I am getting from .NET seem to indicate that is true.)

For one of the systems, I have the ability to make all of my database objects serializable so that's great. Hashes get generated, everything gets synchronized, and the world is beautiful!

For another system, things are not so great. I am passed a database context from entity framework 4 (code first) model and the entities are NOT serialized.

When I attempt to cast as byte using something like the following, .NET complains and throws a minor tantrum--all the while refusing to give me the nice little byte array I so politely asked for.

foreach(var dataItem in context.TableName)
{
    var byteArray = (byte[]) dataItem;
}

Ok. No problem.

I have myself a nice little extension method which I thought might do the trick.

public static byte[] ObjectToByteArray<T>(this T obj)
{
    if (obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();

    bf.Serialize(ms, obj);
    return ms.ToArray();
}

But oh no! If the object (the Entity) is not serializable, this routine throws me another nice little (and totally expected) exception.

So... I modify the routine and add a where clause to the method definition like so.

public static byte[] ObjectToByteArray<T>(this T obj) where T : ISerializable
{
    if (obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();

    bf.Serialize(ms, obj);
    return ms.ToArray();
}

The only problem is that now I am back to square one where all of my objects need to be serializable to get a byte array.

Hmmm. Not good.

So I put together a hack to iterate through all of the object's properties and generate a string representation from which I could build a byte array. It was UGLY and INEFFICIENT but it kind of sort of did the trick.

public static string ComputeMD5Hash<T>(this T input)
{
    StringBuilder sb = new StringBuilder();

    Type t = input.GetType();
    PropertyInfo[] properties = t.GetProperties();

    foreach (var property in properties)
    {
        sb.Append(property.Name);
        sb.Append("|");
        object value = property.GetValue(input, null);
        if (value != null)
        {
            sb.Append(value);
        }
        sb.Append("|");
    }

    return MD5HashGenerator.GenerateKey(sb.ToString());
}

But...

After all that, what I still really would like to be able to is efficiently and properly to create a byte array from an object whose class is not marked as serializable. What is the best way to accomplish this?

Thank you in advance!

解决方案

create a byte array from an object whose class is not marked as serializable

You can use protobuf-net v2 to accomplish this. Download the zip then reference the protobuf-net assembly.

Consider this simple class definition we want to serialize:

public class Person
{
    public string Firstname { get; set; }
    public string Surname { get; set; }
    public int Age { get; set; }
}

You can then serialize this as a byte array:

var person = new Person {Firstname = "John", Surname = "Smith", Age = 30};
var model = ProtoBuf.Meta.TypeModel.Create();
//add all properties you want to serialize. 
//in this case we just loop over all the public properties of the class
//Order by name so the properties are in a predictable order
var properties = typeof (Person).GetProperties().Select(p => p.Name).OrderBy(name => name).ToArray();
model.Add(typeof(Person), true).Add(properties);

byte[] bytes;

using (var memoryStream = new MemoryStream())
{
    model.Serialize(memoryStream, person);
    bytes = memoryStream.GetBuffer();
}

The protobuf-net serializer will serialize much faster and produce a smaller byte[] array than BinaryFormatter

caveat 1 This will only (in its current form) serialize the public properties of your class, which looks ok for your usage.
caveat 2 This is considered brittle because adding a new property to Person may mean you are unable to deserialize a Person object that was serialized with the prior TypeModel.

这篇关于将非序列化的类字节数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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