【问题标题】:SerializationException when deserializing反序列化时出现SerializationException
【发布时间】:2013-04-12 09:23:50
【问题描述】:

此代码从 SQLite 反序列化对象。我从 DBinaryData (BLOB) 字段中获取序列化对象。但是获取 System.Runtime.Serialization.SerializationException: end of stream 在解析完成之前遇到。如何解决这个问题?

    public void Dump()
    {
        try
        {
            const string databaseName = @"C:\Code\C#\WcfService\WcfService\mainDB.db3";
            SQLiteConnection connection = new SQLiteConnection(string.Format("Data Source={0};", databaseName));
            connection.Open();
            try
            {
                SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES ('" + DateTime.Now.ToString() + "', '" + GetSerializedMessages() + "')", connection);
                command.ExecuteNonQuery();
            }
            finally
            {
                connection.Close();
            }
        }
        catch (Exception e)
        {
            Logger.Log(e.Message);
        }
    }

    public void Restore()
    {
        try
        {
            const string databaseName = @"C:\Code\C#\WcfService\WcfService\mainDB.db3";
            SQLiteConnection connection = new SQLiteConnection(string.Format("Data Source={0};", databaseName));
            connection.Open();
            try
            { 
                SQLiteCommand command = new SQLiteCommand("SELECT * FROM dump ORDER BY DId DESC limit 1", connection);
                SQLiteDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                     Queue<Message> deserializedData = GetDeserializedMessages((byte[])reader["DBinaryData"]);
                     var data = MergeQueueMessage(deserializedData);
                     Logger.Log(data.ToString());
                }
            }
            finally
            {
                connection.Close();
            }
        }
        catch (Exception e)
        {
            Logger.Log(e.Message);
        }
    }

    public byte[] GetSerializedMessages()
    {
        byte[] result = null;

        MemoryStream memoryStream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();

        try
        {
            lock (MessageQueue.Instance.Messages)
            {
                formatter.Serialize(memoryStream, MessageQueue.Instance.Messages);
            }
            result = new byte[memoryStream.GetBuffer().Length];
            memoryStream.GetBuffer().CopyTo(result, 0);
        }
        catch (SerializationException e)
        {
            Logger.Log("Failed to serialize. Reason: " + e.Message);
        }
        finally
        {
            memoryStream.Close();
        }
        return result;
    }

    public Queue<Message> GetDeserializedMessages(byte[] source)
    {
        Queue<Message> messages = null;
        using (MemoryStream memoryStream = new MemoryStream(source))
        {
            BinaryFormatter formatter = new BinaryFormatter();
            messages = (Queue<Message>)formatter.Deserialize(memoryStream);
        }
        return messages;
    }

    private IEnumerable<Message> MergeQueueMessage(Queue<Message> source)
    {
        IEnumerable<Message> result = MessageQueue.Instance.Messages.Union(source, new EqualityComparator());
        return result;
    }

【问题讨论】:

  • 在您的编辑中编辑了我的答案 - 您的序列化代码中肯定存在错误。
  • 您的 SQL 中还有一个重大错误

标签: c# .net serialization persistence binary-serialization


【解决方案1】:

根据您的编辑:这是一个错误(但不确定它是否是“那个”错误):

result = new byte[memoryStream.GetBuffer().Length];
memoryStream.GetBuffer().CopyTo(result, 0);

缓冲区的长度无关紧要。如果是重要的memoryStream.Length。坦率地说,这应该只是 result = memoryStream.ToArray(); - 这会给你正确的结果。


SQL 中的另一个错误:

SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES ('" + DateTime.Now.ToString() + "', '" + GetSerializedMessages() + "')", connection);
command.ExecuteNonQuery();

串联从来都不是一个好主意,但在这里它是致命的;因为GetSerializedMessages() 返回null(失败时 - 不是一个好主意;应该刚刚抛出)或byte[],所以这很简单。如果你连接 byte[] 输出不是你所期望的:

byte[] b = {1,2,3};
string s = "a " + b + " c";
// gives: "a System.Byte[] c"

显然不包含您​​想要的实际数据,因此是胡言乱语。理想情况下,您应该在此处为数据和日期使用参数:

SQLiteCommand command = new SQLiteCommand("INSERT into 'dump' ('DTime', 'DBinaryData') VALUES (@when, @data)", connection);
// note: not sure if SQLiteCommand has an "AddWithValue", but the overall usage
// should be something like this
command.Parameters.AddWithValue("when", DateTime.Now);
command.Parameters.AddWithValue("data", GetSerializedMessages());
command.ExecuteNonQuery();

最后:不要吞下问题;您的序列化代码应该(IMO)更像

public byte[] GetSerializedMessages()
{
    try {
        using(MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            // skipped: serialize etc
            return memoryStream.ToArray();
        }
    } catch(Exception ex) {
        Logger.Log("Failed to serialize. Reason: " + ex.Message);
        throw; // it doesn't stop being a problem just because we logged it
    }
}

首先要看的是您输出byte[](通过reader["DBinaryData"])是否与您最初序列化时的byte[] 100% 相同。如果您没有对此进行测试,则所有赌注都将取消。从错误中,听起来它们并不相同 - 这可能是因为:

  • 序列化和存储数据的代码出错
  • 数据库存储中的截断
  • 读取 BLOB 时截断(某些连接会限制一次性获取的数量)
  • 获取和反序列化数据的代码出错

前两个是完全致命的:如果是那些 - 数据就是 toast。

在集成测试中比较两个byte[] 的一种懒惰方法是比较十六进制:

// here expected should be the raw data just after serializing; actual should
// be what you get after storing it in the db and fetching it back, using
// your code
Assert.AreEqual(BitConverter.ToString(expected), BitConverter.ToString(actual));

它给出了任何增量的一个很好的十六进制输出。你没有展示你是如何序列化和存储消息的,所以我不能告诉你那里是否有任何明显的问题,但是请参阅http://marcgravell.blogspot.com/2013/02/how-many-ways-can-you-mess-up-io.html这里的常见问题列表。

最后,我强烈建议:停止使用BinaryFormatter。请参阅questions like this 以了解其他人的痛苦:基本上即使是很小的更改(或者有时只是重建),他们也无法取回数据。基于合约的序列化器会更安全——我倾向于 protobuf-net,但我有很大的偏见。

【讨论】:

    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    • 1970-01-01
    • 2012-02-11
    • 2010-10-08
    • 1970-01-01
    • 2023-04-06
    相关资源
    最近更新 更多