Um dos novos recursos introduzidos na versão 2.1.0 do SDK do Couchbase .NET foi o suporte para substituir o serializador padrão por seu próprio serializador personalizado, estendendo a interface ITypeSerializer e fornecendo seu próprio serializador ou seu favorito. Neste artigo, mostraremos como fazer isso e forneceremos um exemplo (clonável a partir de couchbase-net-contrib projeto em Couchbaselabs) usando um serializador JSON muito rápido chamado Jil.
Por padrão, o SDK .NET usa o popular serializador NewtonSoft.NET JSON, principalmente porque é um projeto com recursos completos, amplamente usado e com bom suporte. Em geral, o desempenho fornecido pelo NewtonSoft é bom o suficiente para a maioria dos projetos; em alguns casos, porém, você pode querer usar um serializador JSON mais rápido, como o Jil.
O Jil é baseado no Sigil, uma biblioteca de geração de IL à prova de falhas, e fornece um serializador de JSON de uso geral altamente otimizado e que usa vários "truques" para melhorar o desempenho, como evitar alocações (portanto, sem GC) e otimizar a ordem de acesso dos membros para que a CPU não precise ficar parada enquanto aguarda os valores na memória. Você pode ler mais sobre o Jil e o Sigil aqui e aqui.
Etapas necessárias
Estender o serializador padrão é relativamente fácil:
- Primeiro, você fornece sua própria implementação do Couchbase.Core.Serializaton.ITypeSerializer.
- Em seguida, substitua o DefaultSerializer na classe Couchbase.Configuration.Client.ClientConfiguration. É isso aí!
- Opcionalmente, você pode usar o Web.Config ou o App.Config para registrar o novo ITypeSerializer para que o SerializerFactory crie o serializador em tempo de execução.
Implementação do ITypeSerializer
A interface ITypeSerializer é usada pelo transcodificador (ITypeTranscoder) para lidar com a serialização e a desserialização do corpo do pacote do Memcached. O transcodificador usa o Type dos parâmetros genéricos da operação, o OperationCode da operação e os "sinalizadores" codificados no cabeçalho do Memcached para determinar em qual Type o corpo deve ser desserializado e para determinar quais sinalizadores devem ser codificados no pacote durante a serialização.
Embora pareça complexo, tudo é muito simples: se o tipo do corpo for um objeto ou primitivo (int, ulong etc.), o corpo será codificado com um sinalizador JSON e tratado como JSON. Sim, os inteiros são tratados como JSON válido! Se o tipo do corpo for uma string, ele será tratado como uma string e codificado ou decodificado em UTF 8. Por fim, as matrizes de bytes são passadas diretamente e simplesmente anexadas ao pacote ou lidas do pacote e atribuídas ao campo Value do IOperationResult, se a operação tiver um corpo. Portanto, qualquer valor que não seja Strings e matrizes de bytes será serializado ou desserializado usando o ITypeSerializer.
O ITypeSerializer tem as seguintes assinaturas de método que você deve implementar:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
public interface ITypeSerializer { T Deserialize(byte[] buffer, int offset, int length); T Deserialize(Stream stream); byte[] Serialize(object obj); } |
Uma sobrecarga para Deserialize recebe um Stream e a outra uma matriz de bytes, offset e comprimento. Serialize recebe apenas uma referência System.Object, que é o valor a ser serializado em uma matriz de bytes para transporte.
A listagem de código do JilSerializer tem a seguinte aparência:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public class JilSerializer : ITypeSerializer { ... public T Deserialize(Stream stream) { using (var reader = new StreamReader(stream)) { return JSON.Deserialize(reader, _options); } } public T Deserialize(byte[] buffer, int offset, int length) { T value = default (T); if (length == 0) return value; using (var ms = new MemoryStream(buffer, offset, length)) { using (var sr = new StreamReader(ms)) { if (typeof(T).IsValueType && (!typeof(T).IsGenericType || typeof(T).GetGenericTypeDefinition() != typeof(Nullable<>))) { var type = typeof (Nullable<>).MakeGenericType(typeof (T)); object nullableVal = JSON.Deserialize(sr, type, _options); value = nullableVal == null ? default(T) : (T)nullableVal; } else { value = JSON.Deserialize(sr, _options); } } } return value; } public byte[] Serialize(object obj) { using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms)) { JSON.Serialize(obj, sw, _options); } return ms.ToArray(); } } } |
Observe que o código é praticamente idêntico à lógica do DefaultSerializer, com a exceção de que estamos usando a classe Jil JSON em vez das classes de serialização do Newtonsoft.NET; embora o mecanismo de serialização seja diferente, as regras são as mesmas.
Configuração do SDK para usar o serializador personalizado
Como mencionado anteriormente, o SDK do Couchbase .NET foi atualizado na versão 2.1.0 para permitir serializadores personalizados. Isso é feito substituindo a fábrica de serializadores padrão na ClientConfiguration, que é apenas uma propriedade Func: qualquer coisa que corresponda à assinatura será criada e usada para serialização. Em uma observação lateral, as SerializationSettings e DeserializationSettings foram descontinuadas e não são mais usadas.
Aqui está um exemplo:
|
1 2 3 4 5 6 7 8 9 10 11 |
var config = new ClientConfiguration { Serializer = () => new JilSerializer() }; var cluster = new Cluster(config); var bucket = cluster.OpenBucket(); |
Embora você possa fornecer uma lógica complexa para a criação do ITypeSerializer, a abordagem mais simples é suficiente.
Se você quiser usar o SerializationFactory para instanciar o serializador, basta fornecer o código de inicialização em seu Web.Config ou App.Config:
|
1 |
<!--?xml version="1.0" encoding="utf-8" ?--> |
Para usar a configuração personalizada em seu código, você chamará o CTOR da classe Cluster, que recebe um caminho de cadeia de caracteres que corresponde à sua configuração:
|
1 2 3 4 |
var cluster = new Cluster("couchbaseClients/couchbase"); var bucket = cluster.OpenBucket(); |
Quando você fizer isso, todos os Buckets abertos a partir do objeto Cluster usarão o JilSerializer em vez do serializador padrão do NewtonSoft.NET.
Conclusão
Com os novos pontos de extensão adicionados ao SDK do Couchbase .NET, é trivial escrever e usar serializadores JSON personalizados usando a interface ITypeSerializer. O serializador JSON do Jil parece impressionante e é fácil de usar; espero que um membro da comunidade crie um para o ServiceStack.Text e o envie para o projeto Couchbase. NET Contrib Project!