【问题标题】:protobuf-net returns null when calling Deserializeprotobuf-net 在调用 Deserialize 时返回 null
【发布时间】:2013-11-08 21:29:32
【问题描述】:

我的最终目标是使用 protobuf-net 和 GZipStream 来尝试压缩 List<MyCustomType> 对象以存储在 SQL Server 的 varbinary(max) 字段中。我正在进行单元测试,以了解一切如何工作和组合在一起。

目标 .NET 框架是 3.5。

我目前的流程是:

  1. 使用 protobuf-net 序列化数据(良好)。
  2. 使用 GZipStream 压缩来自 #1 的序列化数据(好)。
  3. 将压缩数据转换为 base64 字符串(好)。

此时,第 3 步中的值将存储在 varbinary(max) 字段中。我无法控制这一点。这些步骤需要采用 base64 字符串并将其反序列化为具体类型。

  1. 将 base 64 字符串转换为 byte[](好)。
  2. 使用 GZipStream 解压缩数据(好)。
  3. 使用 protobuf-net 反序列化数据(错误)。

有人可以帮助解释为什么对Serializer.Deserialize<string> 的调用返回null?我坚持这一点,希望一组新的眼睛会有所帮助。

FWIW,我使用 List<T> 尝试了另一个版本,其中 T 是我创建的自定义类,我 Deserialize<> 仍然返回 null。

FWIW 2,data.txt 是一个 4MB 的纯文本文件,位于我的 C: 上。

    [Test]
    public void ForStackOverflow()
    {
        string data = "hi, my name is...";
        //string data = File.ReadAllText(@"C:\Temp\data.txt");

        string serializedBase64;

        using (MemoryStream protobuf = new MemoryStream())
        {
            Serializer.Serialize(protobuf, data);

            using (MemoryStream compressed = new MemoryStream())
            {
                using (GZipStream gzip = new GZipStream(compressed, CompressionMode.Compress))
                {
                    byte[] s = protobuf.ToArray();
                    gzip.Write(s, 0, s.Length);
                    gzip.Close();
                }

                serializedBase64 = Convert.ToBase64String(compressed.ToArray());
            }
        }

        byte[] base64byteArray = Convert.FromBase64String(serializedBase64);

        using (MemoryStream base64Stream = new MemoryStream(base64byteArray))
        {
            using (GZipStream gzip = new GZipStream(base64Stream, CompressionMode.Decompress))
            {
                using (MemoryStream plainText = new MemoryStream())
                {
                    byte[] buffer = new byte[4096];
                    int read;

                    while ((read = gzip.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        plainText.Write(buffer, 0, read);
                    }

                    // why does this call to Deserialize return null?
                    string deserialized = Serializer.Deserialize<string>(plainText);

                    Assert.IsNotNull(deserialized);
                    Assert.AreEqual(data, deserialized);
                }
            }
        }
    }

【问题讨论】:

  • 您知道,对于字符串,您在每一步都添加开销,对吧?但是...看起来
  • 目标是获取尽可能小的值以存储在数据库中。当前的问题是我们将包含大约 10K 项的 List 序列化为 XML 并存储在数据库中。这已经膨胀到表中每行大约 4MB。我愿意接受其他建议,但我有一些我无法解决的问题。 #1,SQL Server 字段是 VARBINARY 和 #2,用于写入该字段的方法需要一个字符串。
  • 要明确一点:protobuf-net 将在 CustomType 上做一个公平的工作,但它在单独存储 string 数据时并不是非常理想。另外:base-64 步骤对我来说似乎很奇怪......如果你想存储 varbinary 数据 - 为什么需要 base-64?
  • 我使用 base64 是因为我唯一可用于存储到 VARBINARY 字段的方法需要一个字符串。您对如何将 MemoryStream 中的数据获取到 base64 以外的字符串有其他建议吗?
  • @Rodemoyer 不,base-64 是我的建议。看起来……很不幸。

标签: c# serialization protobuf-net


【解决方案1】:

因为你写信后没有回退plainText。实际上,整个 Stream 是不必要的 - 这行得通:

using (MemoryStream base64Stream = new MemoryStream(base64byteArray))
{
    using (GZipStream gzip = new GZipStream(
        base64Stream, CompressionMode.Decompress))
    {
        string deserialized = Serializer.Deserialize<string>(gzip);

        Assert.IsNotNull(deserialized);
        Assert.AreEqual(data, deserialized);
    }
}

同样,这应该适用于序列化:

using (MemoryStream compressed = new MemoryStream())
{
    using (GZipStream gzip = new GZipStream(
        compressed, CompressionMode.Compress, true))
    {
        Serializer.Serialize(gzip, data);
    }

    serializedBase64 = Convert.ToBase64String(
        compressed.GetBuffer(), 0, (int)compressed.Length);
}

【讨论】:

  • 太棒了,这一切都很完美,我很感激你展示了我能如何浓缩正在发生的事情。唯一的问题是对 Convert.ToBase64String 的调用会引发 ObjectDisposedException,这是因为 GZipStream 是如何与 MemoryStream 一起工作的。在这种情况下,您必须使用 ToArray()。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-22
  • 1970-01-01
  • 2015-03-29
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2020-08-15
相关资源
最近更新 更多