反序列化抛出异常:SQLite存储List<float>为BLOB后无法重建问题
问题:将List作为BLOB存入SQLite后反序列化失败
我尝试将List<float>作为BLOB存储到SQLite数据库,但重建List时遇到System.Runtime.Serialization.SerializationException异常,提示“End of Stream encountered before parsing was completed”。插入数据的代码运行正常,但反序列化时出错,同时我也好奇为什么5个float序列化后会生成237字节的数据。
插入数据代码:
public void InsertRow() { List<float> myList = new List<float>(); myList.Add(12); myList.Add(13); myList.Add(14); myList.Add(15); myList.Add(16); var binFormatter = new BinaryFormatter(); var mStream = new MemoryStream(); binFormatter.Serialize(mStream, myList); SQLiteCommand command = new SQLiteCommand(m_dbConnection); byte[] data = mStream.ToArray(); command.CommandText = "insert into photos (photo) values (@photo)"; command.Parameters.Add("@photo", DbType.Binary, 237).Value = data; command.ExecuteNonQuery(); }
反序列化代码(出错):
public void GetRow() { string sql = "select photo from photos where id = 1"; SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection); using (var reader = command.ExecuteReader()) { while (reader.Read()) { byte[] buffer = GetBytes(reader); var mStream = new MemoryStream(); var binFormatter = new BinaryFormatter(); mStream.Write(buffer, 0, 237); // 问题核心所在 mStream.Position = 0; var myObject = binFormatter.Deserialize(mStream) as List<float>; } } }
补充的GetBytes函数:
static byte[] GetBytes(SQLiteDataReader reader) { const int CHUNK_SIZE = 2 * 1024; byte[] buffer = new byte[CHUNK_SIZE]; long bytesRead; long fieldOffset = 0; using (MemoryStream stream = new MemoryStream()) { while ((bytesRead = reader.GetBytes(0, fieldOffset, buffer, 0, buffer.Length)) > 0) { stream.Write(buffer, 0, (int)bytesRead); fieldOffset += bytesRead; } return stream.ToArray(); } }
问题分析与解决
1. 反序列化失败的核心原因
你在反序列化时犯了一个低级但致命的错误:硬编码了写入MemoryStream的字节数为237。
你的GetBytes函数已经完美完成了它的工作——从数据库中读取了完整的BLOB数据,返回的buffer是完整的序列化字节数组。但你强制只写入237字节,这会导致两种情况:
- 如果实际存储的字节数小于237:写入的是不完整的序列化流,反序列化时自然会报“流提前结束”的错误
- 如果实际存储的字节数大于237:同样只写入了部分数据,反序列化无法解析完整的对象结构
修正后的反序列化代码:
public void GetRow() { string sql = "select photo from photos where id = 1"; SQLiteCommand command = new SQLiteCommand(sql, m_dbConnection); using (var reader = command.ExecuteReader()) { while (reader.Read()) { byte[] buffer = GetBytes(reader); // 直接用完整的字节数组初始化MemoryStream,自动定位到流的起始位置 using (var mStream = new MemoryStream(buffer)) { var binFormatter = new BinaryFormatter(); var myObject = binFormatter.Deserialize(mStream) as List<float>; } } } }
另外,插入数据时你也硬编码了参数长度为237,这完全没必要,SQLite会自动处理BLOB的长度,改成下面这样更灵活:
command.Parameters.Add("@photo", DbType.Binary).Value = data;
2. 为什么5个float序列化后是237字节?
BinaryFormatter的序列化逻辑远不止存储原始数据,它会携带大量元数据:
- 目标类型的完整标识(包括命名空间、程序集版本信息)
- List的结构元数据(比如容量、元素数量)
- .NET序列化框架的额外标记、头信息
每个float本身是4字节,5个加起来才20字节,但加上这些元数据后,总字节数就会膨胀到237左右。如果想减少序列化后的字节体积,可以考虑更轻量的方案:
- 手动将
List<float>转换为byte[](逐个float转4字节直接拼接) - 使用
System.Text.Json、MessagePack这类高效的序列化库
内容的提问来源于stack exchange,提问作者Q-bertsuit
相关产品推荐
相关产品推荐

