Custom Jackson JSON serializer/deserializer from Circe

| 1 minute | Comments

Snippet for when you’re using Circe and want to define custom Jackson serializers/deserializers from Circe’s codec definitions.

It’s not very efficient, as deserialization seems to parse the JSON in both Jackson and Circe, but I don’t have better ideas.

import com.fasterxml.jackson.core.{ JsonGenerator, JsonParser, TreeNode }
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.databind.{ DeserializationContext, SerializerProvider }
import io.circe.parser.decode
import io.circe.syntax._
import io.circe.{ Decoder, Encoder }
import scala.reflect.ClassTag

class JacksonSerializerFromCirce[A: ClassTag: Encoder]
extends StdSerializer[A](
    implicitly[ClassTag[A]].runtimeClass.asInstanceOf[Class[A]]
  ) {
  override def serialize(
    value: A,
    gen: JsonGenerator,
    provider: SerializerProvider
  ): Unit = {
    val json = value.asJson.noSpaces
    gen.writeRawValue(json)
  }
}

class JacksonDeserializerFromCirce[A: ClassTag: Decoder]
  extends StdDeserializer[A](
    implicitly[ClassTag[A]].runtimeClass.asInstanceOf[Class[A]]
  ) {
  override def deserialize(
    p: JsonParser,
    ctxt: DeserializationContext
  ): A = {
    decode[A](p.readValueAsTree[TreeNode]().toString) match {
      case Right(a) => a
      case Left(e) => throw e
    }
  }
}

Usage:

import io.circe.Codec
import io.circe.generic.semiauto.deriveCodec

@JsonSerialize(using = classOf[Sample.Serializer])
@JsonDeserialize(using = classOf[Sample.Deserializer])
final case class Sample(
  name: String,
  isActive: Boolean
)

object Sample {
  implicit val codec: Codec[Sample] =
    deriveCodec

  class Serializer
    extends JacksonSerializerFromCirce[Sample]
  class Deserializer
    extends JacksonDeserializerFromCirce[Sample]
}
| Written by
Tags: Scala | Snippet