Serializando objetos en MONGODB Java (POJO) usando Codecs (II)

En el articulo anterior, les traté de mostrar como guardar y recuperar documentos en MongoDB … sin mucho éxito.

El problema es que el Java POJO utiliza enumeraciones y la base de datos no puede por si sólo encargarse de su manejo.

Después de leer otra vez la guía de Jackson con las anotaciones ‘@JsonDeserialize’ ‘@JsonSerialize’ y MongoDB con Java Codecs entendí que esta es la estrategia:

  • En Jackson, debemos guardar y leer las enumeraciones como cadena de caracteres. De hecho mi definición contiene una representación mas amigable que la que estaba usando. Esto lo voy a usar más tarde para un servicio web que estoy escribiendo y no tiene nada que ver con MongoDB 🙂
  • En MongoDB registramos un Codec que se encarga de leer la enumeración desde una cadena de caracteres y la escribe de vuelta como una cadena de caracteres

Todo se ve más claro en código, así que allí vamos. Lo primero es anotar nuestro POJO con Jackson (sólo muestro el atributo StatusEnumType):

public class ItemType {
    @JsonDeserialize(using = StatusEnumTypeDeserializer.class)
    @JsonSerialize(using = StatusEnumTypeSerializer.class)
    @XmlElement(required = true)
    @XmlSchemaType(name = "token")
    protected StatusEnumType status;

La anotación registra el atributo con las clases que van a leer y a escribir la enumeración de forma que pueda ser entendida por nuestro ObjectMapper en Jackson.

La clase que codifica:

package com.kodegeek.cvebrowser.persistence.serializers;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;

import java.io.IOException;

public class SimplePhaseEnumTypeSerializer extends JsonSerializer {
    @Override
    public void serialize(SimplePhaseEnumType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (! StringChecker.isMissing(value.value())) {
            gen.writeString(value.value());
        } else {
            gen.writeNull();
        }
    }
}

Y la que decodifica:

package com.kodegeek.cvebrowser.persistence.serializers;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;

import java.io.IOException;

public class SimplePhaseEnumTypeDeserializer extends JsonDeserializer {
    @Override
    public SimplePhaseEnumType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        SimplePhaseEnumType value = null;
        try {
            value = SimplePhaseEnumType.fromValue(p.getValueAsString());
        } catch (IllegalArgumentException eaexp) {
            eaexp.printStackTrace();
        }
        return value;
    }
}

No hay que hacer nada más para que Jackson funcione. En cuanto a MongoDB, debemos escribir una clase que implemente “Codec”. Esta clase se encarga de codificar y descodificar nuestra enumeración:

package com.kodegeek.cvebrowser.persistence.serializers;

import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;

public class SimplePhaseEnumTypeCodec implements Codec{
    @Override
    public SimplePhaseEnumType decode(BsonReader reader, DecoderContext decoderContext) {
        return SimplePhaseEnumType.fromValue(reader.readString());
    }

    @Override
    public void encode(BsonWriter writer, SimplePhaseEnumType value, EncoderContext encoderContext) {
        writer.writeString(value.value());
    }

    @Override
    public Class getEncoderClass() {
        return SimplePhaseEnumType.class;
    }
}

El paso final es registrar nuestros codecs con el registro, de manera que estos sean llamados cuando procesemos este tipo de datos:

    /**
     * MongoDB could not make this any simpler ;-)
     * @return a Codec registry
     */
    public static CodecRegistry getCodecRegistry() {
        final CodecRegistry defaultCodecRegistry = MongoClient.getDefaultCodecRegistry();
        final CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(packages).build();
        final CodecRegistry cvePojoCodecRegistry = CodecRegistries.fromProviders(pojoCodecProvider);
        // Aqui e stan los nuevos codecs, el del ejemplo es "SimplePhaseEnumTypeCodec"
        final CodecRegistry customEnumCodecs = CodecRegistries.fromCodecs(
                new SimplePhaseEnumTypeCodec(),
                new StatusEnumTypeCodec(),
                new TypeEnumTypeCodec()
        );
        return CodecRegistries.fromRegistries(defaultCodecRegistry, customEnumCodecs, cvePojoCodecRegistry);
    }