Serializando Objetos C# com ProtoBuf

8 jan

Fala galera!

Antes de começar o post, gostaria de compartilhar com vocês que tive a grande honra de me tornar MVP (Microsoft Most Valuable Professional). Com isso, poderei ajudar ainda mais a comunidade com meus artigos, eventos e etc.  Deixo o meu muito obrigado a todos que ajudaram de alguma forma, lendo meus artigos, comentando ou indo a eventos no qual eu ajudei a organizar.

Neste primeiro post do ano, irei falar de uma biblioteca chamada Protobuf.

Essa biblioteca é de extrema valia quando precisamos serializar uma grande quantidade de dados e com uma performance excepcional. Muitas das vezes fazer parse de JSON ou XML não é ideal quando estamos falando de objetos grandes com alguns megas de dados. Neste tipo de cenário uma serialização em formato binário é muito mais eficiente.

A biblioteca Protobuf tem algumas particularidades como:

  • [ProtoContract] – Indica que a classe será serializada usando o Protobuf.
  • [ProtoMember] – Indica que a propriedade/field deverá ser serializada usando o Protobuf
  • [ProtoIgnore] – Indica que a propriedade/field será ignorada quando uma serialização ocorrer

Outras características do Protobuf

  • A serialização do Protobuf ignora a interface ISerializable
  • Não é possível customizar a serialização do Protobuf usando a interface ISerializable

Show me the code!

Para usar o Protobuf, basta instalar usando o Package Manager Console através do comando abaixo:

Install-Package protobuf-net

 

Para esse exemplo, criei uma classe chamada Cliente e Endereço e anotei com os atributos do Protobuf. Conforme exemplo abaixo:

[ProtoContract()]
public class Cliente
{
    [ProtoMember(1)]
    public string PrimeroNome { get; set; }

    [ProtoMember(2)]
    public string UltimoNome { get; set; }

    [ProtoMember(3)]
    public DateTime DataNascimento { get; set; }

    [ProtoMember(4)]
    public List<Endereco> Enderecos { get; set; }
}

[ProtoContract()]
public class Endereco
{
    [ProtoMember(1)]
    public string Logradouro { get; set; }

    [ProtoMember(2)]
    public string Complemento { get; set; }

    [ProtoMember(3)]
    public string Bairro { get; set; }

    [ProtoMember(4)]
    public string Cidade { get; set; }

    [ProtoMember(5)]
    public string Pais { get; set; }

}

Feito isso, criei uma classe estática que servirá para serializar e deserializar os objetos.

public static class ProtoSerialize
{
       public static byte[] Serialize<T>(T obj) where T : class
       {
           if (obj == null)
               return new byte[] { };

           using(var stream = new MemoryStream())
           {
               ProtoBuf.Serializer.Serialize<T>(stream, obj);
               return stream.ToArray();
           }
       }

       public static T Deserialize<T>(byte[] data) where T : class
       {
           if (data == null)
               return default(T);

           using (var stream = new MemoryStream(data))
           {
               return ProtoBuf.Serializer.Deserialize<T>(stream);
           }
       }
}

Para rodar nosso projeto eu criei um Console Application, nele temos um teste de desempenho usando a biblioteca Newtonsoft.json umas das mais famosas bibliotecas para se converter objetos C# em JSON.

static void Main(string[] args)
      {
          var stopWatch = Stopwatch.StartNew();

          for (int i = 0; i < 5000000; i++)
          {
              var obj = CreateCliente(i);
              Newtonsoft.Json.JsonConvert.SerializeObject(obj);
          }

          stopWatch.Stop();

          TimeSpan ts = stopWatch.Elapsed;

          string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);

          Console.WriteLine("RunTime Json " + elapsedTime);

          var stopProto = Stopwatch.StartNew();

          for (int i = 0; i < 5000000; i++)
          {
              var obj = CreateCliente(i);
              Protobuf.Serializer.ProtoSerialize.Serialize<Cliente>(obj);
          }

          stopProto.Stop();

          TimeSpan tsProto = stopProto.Elapsed;

          string elapsedTimeProto = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", tsProto.Hours, tsProto.Minutes, tsProto.Seconds, tsProto.Milliseconds / 10);

          Console.WriteLine("RunTime Proto " + elapsedTimeProto);

          Console.Read();
      }

      private static Cliente CreateCliente(int i)
      {
          return new Object.Cliente()
          {
              DataNascimento = DateTime.Now,
              PrimeroNome = $"Cliente{i}",
              UltimoNome = $"UltimoNome{i}",
              Enderecos = new List<Endereco>()
              {
                  new Object.Endereco()
                  {
                      Bairro = $"Bairro{i}",
                      Cidade = $"Cidade{i}",
                      Complemento = $"Complemento{i}",
                      Logradouro = $"Logradouro{i}",
                      Pais = $"Pais{i}"
                  }
              }

          }; 
      }

A velocidade de serialização dos objetos usando o Protobuf chega a ser 17s mais rápido em relação ao Newtonsoft.json

Abaixo uma gráfico de comparação de performance em relação a outras bibliotecas.

O código deste post está no meu GitHub clicando aqui

A documentação do Protobuf pode se encontrada clicando aqui

Abs e até a próxima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *