【问题标题】:C# Binary formatter properties have same valueC# 二进制格式化程序属性具有相同的值
【发布时间】:2017-04-05 09:43:41
【问题描述】:

当我使用 binaryformatter 序列化对象时,我遇到了一个小问题。序列化的重点是可以将值传递到需要字节数组的哈希函数中。

我的过程是,我读取文件,使用newtonsoft Json将json文件转换为POCO对象,进行一些检查,根据需要更新值并以Json格式保存回同一文件。

检查包括验证文件中的哈希值与进程开始时重新生成的内容是否匹配。我采取的步骤是,读取文件,转换为 POCO,使用二进制格式化程序进行序列化,生成哈希值,比较两个值,如果正确,更新数据并将新的哈希值和对象作为 Json 保存到文件中。

但是,当我使用二进制格式化程序序列化对象时遇到了问题。如果对象具有值相同的属性,则序列化程序的字节输出与从文件读入数据到写出数据时不同。由于字节数组的值不同,哈希值也不同。此外,如果属性的值不同,则会生成相同的哈希值,因此不会出现问题。

我的问题是为什么当对象被读取和写入文件时,具有相同的值会导致字节值不同。

[Serializable]
public class UserAuthorisationData
{
    public string surname { get; set; }
    public string forename { get; set; }
    public string initials { get; set; }  

    public UserAuthorisationData()
    {
        surname = "";
        forename = "";
        initials = "";

    }
}

例如

var objectA = new UserAuthorisationData()
objectA.surname = "Fred";
objectA.forename = "John";
objectA.initials = "FJ"; 

var objectB = new UserAuthorisationData()
objectB.surname = "John";
objectB.forename = "John";
objectB.initials = "JJ";

在上面的例子中,objectA 的字节数组的值是相同的,当哈希值在数据被写出和文件被读回时产生。

但是,对于 objectB,值不同。

下面的方法将对象转换为字节:

    protected virtual byte[] ObjectToByteArray(object objectToSerialize)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        MemoryStream fs = new MemoryStream();

        try
        {

            lock (locker)
            {                   
                formatter.Serialize(fs, objectToSerialize);
                Logger.Debug($"Successfully converted object to bytes for serialization.");
            }
            File.WriteAllBytes(@"C:\ali.kamal\User1.txt", fs.ToArray());

            return fs.ToArray();
        }
   }

在对象上调用方法

ObjectToByteArray(objectA);
ObjectToByteArray(objectB);

更新 1

谢谢哈迪。哈希码是使用 Microsoft 的 HMACSHA256.computeHash 方法生成的。

protected override string ComputeHash(byte[] objectAsBytes, byte[] key)
    {
        byte[] hashValue = null;
        try
        {
            using (HMACSHA256 hmac = new HMACSHA256(key))
            {
                hashValue = hmac.ComputeHash(objectAsBytes);

            }
        }catch(Exception ex)
        {
            EventLogger.LogEvent($"Could not generate SHA256 hash value, exception:{ex.Message}", EventEntryType.Error);
        }

        return Convert.ToBase64String(hashValue); 
    }

例如

 string hashvalue = ComputeHash(ObjectToByteArray(objectA), ObjectToByteArray("abcd"))

【问题讨论】:

    标签: c# arrays json serialization hash


    【解决方案1】:

    我建议您以不同的方式而不是哈希码方法比较字节:Array1.SequenceEqual(Array2),不清楚您如何比较问题中两个数组的哈希码,但我会假设您正在使用GetHashCode 方法,使用 Array1.GetHashCode() == Array2.GetHashCode() 不适合您的情况,除非您覆盖 HasCode 函数。下面是使用控制台应用程序使用SequenceEqual 方法的示例:

    using System;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var a = new UserData();
                var b = new UserData();
    
                var aS = ObjectToByte(a);
                var bS = ObjectToByte(b);
                Console.WriteLine("A : {0}", a);
                Console.WriteLine("B : {0}", b);
                // result for empty objects are equal
                Console.WriteLine("A == B ? => {0} \n\n", aS.SequenceEqual(bS));
    
                a = new UserData()
                {
                    ForeName = "A",
                    Initials = "CC",
                    SurName = "B",
                };
    
                b = new UserData()
                {
                    ForeName = "AX",
                    Initials = "CC",
                    SurName = "B",
                };
    
                aS = ObjectToByte(a);
                bS = ObjectToByte(b);
    
                Console.WriteLine("A : {0}", a);
                Console.WriteLine("B : {0}", b);
                // result for same data type with same object values are equal
                Console.WriteLine("A == B ? => {0} \n\n", aS.SequenceEqual(bS));
    
                a = new UserData()
                {
                    ForeName = "AX",
                    Initials = "CC",
                    SurName = "B",
                };
                b = a;
    
                aS = ObjectToByte(a);
                bS = ObjectToByte(b);
    
                Console.WriteLine("A : {0}", a);
                Console.WriteLine("B : {0}", b);
                // result for different objects are not equal
                Console.WriteLine("A == B ? => {0} \n\n", aS.SequenceEqual(bS));
            }
    
            static byte[] ObjectToByte(object item)
            {
                var formatter = new BinaryFormatter();
                using (var memory = new MemoryStream())
                {
                    formatter.Serialize(memory, item);
                    return memory.ToArray();
                }
            }
        }
    
        [Serializable]
        public class UserData
        {
            public string SurName { get; set; }
            public string ForeName { get; set; }
            public string Initials { get; set; }
    
            public override string ToString()
            {
                return string.Format("{{SurName: {0} , ForeName:{1}, Initials:{2}}}", SurName ?? "Empty", ForeName ?? "Empty", Initials ?? "Empty");
            }
        }
    }
    

    这是一个工作的Demo

    希望对你有帮助

    【讨论】:

    • 谢谢哈迪。哈希码是使用 Microsoft 的 HMACSHA256.computeHash 生成的。
    • 只要您使用相同的 HMACSHA256 对象,它就给了我相同的结果,var h = new HMACSH256(); var aS = h.ComputeHash(ObjectToByte(a)); var bS = h.ComputeHash(ObjectToByte(b)); 现在 bS.SequenceEqual(aS) 可以正常工作,但是如果您使用不同的 HMACSH256 ComputeHash 对象,它将无法正常工作.
    • 太棒了。但是你知道为什么字节数组的值不同吗?
    • 当您使用 HMACSHA256() 构造函数而不传递密钥时,它使用随机密钥,这就是为什么在我之前的评论中我说结果会有所不同。但是从您的更新 1 中,很明显您正在传递密钥,如果您使用相同的密钥,这应该会给您相同的结果!,我试图重现您的问题并且完全没有问题,请检查我制作的这个示例 @987654322 @
    猜你喜欢
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 2017-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    相关资源
    最近更新 更多