美文网首页
ProtoBuf 序列化结果 长度为 0 的情况

ProtoBuf 序列化结果 长度为 0 的情况

作者: gruan | 来源:发表于2021-11-17 16:59 被阅读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 数组了。

    相关文章

      网友评论

          本文标题:ProtoBuf 序列化结果 长度为 0 的情况

          本文链接:https://www.haomeiwen.com/subject/mooltrtx.html