Una de las nuevas características introducidas en 2.1.0 del SDK .NET de Couchbase fue el soporte para sobreescribir el serializador por defecto con tu propio serializador personalizado extendiendo la interfaz ITypeSerializer y proporcionando tu serializador propio o favorito. En este artículo te mostraremos cómo hacerlo y proporcionaremos un ejemplo (clonable desde couchbase-net-contrib proyecto en Couchbaselabs) utilizando un serializador JSON muy rápido llamado Jil.
Por defecto el .NET SDK utiliza el popular serializador JSON NewtonSoft.NET principalmente porque es un proyecto completo, ampliamente utilizado y bien soportado. En general, el rendimiento proporcionado por el NewtonSoft es lo suficientemente bueno para la mayoría de los proyectos, en algunos casos, sin embargo, es posible que desee utilizar un serializador JSON más rápido como Jil.
Jil se basa en Sigil, una biblioteca de generación de IL a prueba de fallos, y proporciona un serializador JSON de propósito general que está altamente optimizado y utiliza una serie de "trucos" para mejorar el rendimiento, como evitar las asignaciones (por lo tanto no hay GC) y optimizar el orden de acceso a los miembros para que la CPU no tenga que detenerse mientras espera valores en la memoria. Más información sobre Jil y Sigil aquí y aquí.
Pasos necesarios
Extender el serializador por defecto es relativamente fácil:
- Primero proporciona tu propia implementación de Couchbase.Core.Serializaton.ITypeSerializer.
- Luego anula el DefaultSerializer en la clase Couchbase.Configuration.Client.ClientConfiguration. Y ya está.
- Opcionalmente, puede utilizar Web.Config o App.Config para registrar el nuevo ITypeSerializer para que SerializerFactory cree el serializador en tiempo de ejecución.
Implementación de ITypeSerializer
La interfaz ITypeSerializer es utilizada por el transcodificador (ITypeTranscoder) para manejar la serialización y deserialización del cuerpo del paquete Memcached. El transcodificador utiliza el Tipo de los parámetros genéricos de la operación, el OperationCode de la operación y las "banderas" codificadas dentro de la cabecera de Memcached para determinar a qué Tipo deserializar el cuerpo y para determinar qué banderas codificar el paquete durante la serialización.
Aunque parece complejo, es realmente sencillo; si el tipo del cuerpo es un objeto o primitivo (int, ulong,etc), el cuerpo se codifica con una bandera JSON y se trata como JSON. Sí, ¡los enteros se tratan como JSON válido! Si el tipo del cuerpo es una cadena, se trata como una cadena y se codifica o decodifica UTF 8. Por último, las matrices de bytes se pasan directamente y simplemente se añaden al paquete o se leen del paquete y se asignan al campo Value del IOperationResult, si la operación tiene un cuerpo. Por tanto, cualquier valor que no sea una cadena o una matriz de bytes se serializará o deserializará utilizando el serializador ITypeSerializer.
El ITypeSerializer tiene las siguientes firmas de métodos que debes implementar:
1 2 3 4 5 6 7 8 9 10 11 12 |
público interfaz ITypeSerializer { T Deserializar(byte[] búfer, int offset, int longitud); T Deserializar(Corriente flujo); byte[] Serializar(objeto obj); } |
Una sobrecarga para Deserialize toma un Stream y la otra un array de bytes, offset y longitud. Serialize sólo toma una referencia System.Object que es el valor a serializar en una matriz de bytes para el transporte.
El listado de código para el JilSerializer tiene este aspecto:
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 |
público clase JilSerializer : ITypeSerializer { ... público T Deserializar(Corriente flujo) { utilizando (var lector = nuevo StreamReader(flujo)) { devolver JSON.Deserializar(lector, Opciones); } } público T Deserializar(byte[] búfer, int offset, int longitud) { T valor = por defecto (T); si (longitud == 0) devolver valor; utilizando (var ms = nuevo MemoryStream(búfer, offset, longitud)) { utilizando (var sr = nuevo StreamReader(ms)) { si (tipode(T).IsValueType && (!tipode(T).IsGenericType || tipode(T).GetGenericTypeDefinition() != tipode(Anulable<>))) { var tipo = tipode (Anulable<>).MakeGenericType(tipode (T)); objeto nullableVal = JSON.Deserializar(sr, tipo, Opciones); valor = nullableVal == null ? por defecto(T) : (T)nullableVal; } si no { valor = JSON.Deserializar(sr, Opciones); } } } devolver valor; } público byte[] Serializar(objeto obj) { utilizando (var ms = nuevo MemoryStream()) { utilizando (var sw = nuevo StreamWriter(ms)) { JSON.Serializar(obj, sw, Opciones); } devolver ms.ToArray(); } } } |
Observe que el código es casi idéntico a la lógica de DefaultSerializer con la excepción de que estamos utilizando la clase JSON de Jil en lugar de las clases de serialización de Newtonsoft.NET; aunque el mecanismo de serialización es diferente las reglas son las mismas.
Configuración del SDK para utilizar el serializador personalizado
Como se mencionó anteriormente, el SDK .NET de Couchbase fue actualizado en 2.1.0 para permitir serializadores personalizados, esto se hace sobreescribiendo la fábrica de serializadores por defecto en ClientConfiguration, que es sólo una propiedad Func: cualquier cosa que coincida con la firma será creada y usada para la serialización. Como nota al margen, los SerializationSettings y DeserializationSettings han quedado obsoletos y ya no se utilizan.
He aquí un ejemplo:
1 2 3 4 5 6 7 8 9 10 11 |
var config = nuevo ClientConfiguration { Serializador = () => nuevo JilSerializer() }; var grupo = nuevo Grupo(config); var cubo = grupo.OpenBucket(); |
Aunque puede proporcionar una lógica compleja para la creación del ITypeSerializer, el enfoque más simple es suficiente.
Si desea utilizar SerializationFactory para instanciar el serializador, sólo tiene que proporcionar el código de inicialización en su Web.Config o App.Config:
1 |
<!--?xml versión="1.0" codificación="utf-8" ?--> |
Para utilizar la configuración personalizada en su código, llamará al CTOR de la clase Cluster que toma una ruta de cadena que coincide con su configuración:
1 2 3 4 |
var grupo = nuevo Grupo("couchbaseClients/couchbase"); var cubo = grupo.OpenBucket(); |
Una vez hecho esto, cualquier Bucket abierto desde el objeto Cluster utilizará el serializador JilSerializer en lugar del serializador NewtonSoft.NET por defecto.
Conclusión
Con los nuevos puntos de extensión añadidos al SDK .NET de Couchbase, es trivial escribir y usar serializadores JSON personalizados usando la interfaz ITypeSerializer. El serializador JSON de Jil tiene un aspecto impresionante y es fácil de usar; ¡espero que algún miembro de la comunidad cree uno para ServiceStack.Text y lo suba al proyecto Couchbase. ¡NET Contrib Project!