ProtoBuf 序列化結果 長度為 0 的情況

考慮如下聲明:

var t = new Protobuf0Len(){ A = false }
...
...
[ProtoBuf.ProtoContract(ImplicitFields = ProtoBuf.ImplicitFields.AllPublic)]
public class Protobuf0Len
{
    public bool A { get; set; }

    public IEnumerable<int> B { get; set; }

    public int C { get; set; }

    public string D { get; set; }

    public long E { get; set; }

    public DateTime? F { get; set; }
}

變量 t 的各個屬性都是默認的值,A = false, B = null, C = 0, D = null, E = 0, F = null.

用以下方法序列化這個 t, 你將得到一個 0 長度的 byte 數組:

public static byte[] Serialize(object obj)
{
    if (obj == null)
        return null;

    using var msm = new MemoryStream();
    Serializer.Serialize(msm, obj);
    return msm.ToArray();
}

原因

因為, ProtoBuf 對這樣的數據不感興趣,都是默認值, 沒什么值得去序列化的。。。(沒找到官方說明, 我瞎寫的)

ProtoBuf 之所以這樣返回, 是沒有問題的, 因為0 長度的 byte 數組, 可以反序列化成萬物的:

ReadOnlySpan<byte> span = new byte[0];
var t1 = ProtoBuf.Serializer.Deserialize<Protobuf0Len>(span);

變量 t1 里的每個屬性都是默認值。。。

問題

但是,出于程序員特有的嚴謹, 我們拿到結果時, 都會去判斷一下是否非空, 非空時才會進行下一步操作。。。

比如:

// 鍋:
// {A=false, B=null}, 用 Protobuf 序列化后, 就是長度為 0 的字節數組.
// 寫到 redis 里,在讀出來, HasValue 就是 false,
// false 這里就不會執行了, 返回出去就是 null 了.
if (r.HasValue)
{
    //var d = Deserialize<T>(r);
    var d = await DeserializeAsync<T>(r);
    results.Add(d);
}

這就造成了一個隱藏的BUG, 明明是有結果的, 但是卻取不出來。。。

解決方法

為了避免這個問題,序列化反序列化可以用:SerializeWithLengthPrefix / DeserializeWithLengthPrefix

var t = new Protobuf0Len();

byte[] a;
using (var msm = new MemoryStream())
{
    ProtoBuf.Serializer.SerializeWithLengthPrefix<Protobuf0Len>(msm, t, ProtoBuf.PrefixStyle.Base128);
    a = msm.ToArray();
}

using (var msm = new MemoryStream(a))
{
    var t1 = ProtoBuf.Serializer.DeserializeWithLengthPrefix<Protobuf0Len>(msm, ProtoBuf.PrefixStyle.Base128);
}

這樣, 序列化出來的就至少是長度為1的 byte 數組了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。