如何在CIRCE中覆盖默认编解码器



我想将案例类的Array[Byte]字段编码为base64字符串。由于某种原因,CIRCE不会使用默认一个代替将我的编解码器转换为ints的JSON数组。

我该怎么办?这是我的最小代码

import io.circe.generic.JsonCodec
sealed trait DocumentAttribute
@JsonCodec
sealed case class DAAudio(title: Option[String], performer: Option[String], waveform: Option[Array[Byte]], duration: Int) extends DocumentAttribute
@JsonCodec
sealed case class DAFilename(fileName: String) extends DocumentAttribute
object CirceEncodersDecoders {
  import io.circe._
  import io.circe.generic.extras._
  import io.circe.generic.extras.semiauto._
  implicit val arrayByteEncoder: Encoder[Array[Byte]] = Encoder.encodeString.contramap[Array[Byte]] { bytes ⇒
    Base64.getEncoder.encodeToString(bytes)
  }
  val printer: Printer = Printer.noSpaces.copy(dropNullValues = true, reuseWriters = true)
  implicit val config: Configuration = Configuration.default.withDiscriminator("kind").withSnakeCaseConstructorNames.withSnakeCaseMemberNames
  implicit val DocumentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder
  implicit val DocumentAttributeDecoder: Decoder[DocumentAttribute] = deriveDecoder
}
object main {
  def main(args: Array[String]): Unit = {
    import CirceEncodersDecoders._
    import io.circe.parser._
    import io.circe.syntax._
    val attributes: List[DocumentAttribute] = List(
      DAAudio(Some("title"), Some("perform"), Some(List(1, 2, 3, 4, 5).map(_.toByte).toArray), 15),
      DAFilename("filename")
    )
    val j2 = attributes.asJson
    val decoded2 = decode[List[DocumentAttribute]](j2.noSpaces)
    println(decoded2)
  }
}

执行此操作时:

implicit val DocumentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder

circe试图为DAFilenameDAAudio获得合适的Encoder。但是,由于这些已经存在(通过单个类上的@JsonCodec(,因此它不会使用generics和您的 Encoder[Array[Byte]]从头开始重新启动它们。

因此,您可以摆脱 @JsonCodec(因此,与DAFilenameDAAudio一起自动衍生物编解码器以及DocumentAttribute(,或者手动触发重新启动:

implicit val AudioDecoder: Encoder[DAAudio] = deriveEncoder // takes priority over existing one
implicit val DocumentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder // AudioDecoder will be used here

您还需要为Array[Byte]构建Decoder并重复上述Decoder s的过程,否则它将尝试将base64字符串作为INT列表解析,从而导致故障。

它可以接触@JsonCodec注释与Array[Byte]的自定义编码器无法使用。

这是使用circe的所有需要编码和解码的内容:

object CirceEncodersDecoders2 {
  val printer: Printer = Printer.noSpaces.copy(dropNullValues = true, reuseWriters = true)
  implicit val arrayByteEncoder: Encoder[Array[Byte]] =
    Encoder.encodeString.contramap[Array[Byte]](Base64.getEncoder.encodeToString)
  implicit val arrayByteDecoder: Decoder[Array[Byte]] =
    Decoder.decodeString.map[Array[Byte]](Base64.getDecoder.decode)
  implicit val config: Configuration = Configuration.default.withDiscriminator("kind").withSnakeCaseConstructorNames.withSnakeCaseMemberNames
  implicit val audioEncoder: Encoder[DAAudio] = deriveEncoder[DAAudio]
  implicit val audioDecoder: Decoder[DAAudio] = deriveDecoder[DAAudio]
  implicit val filenameEncoder: Encoder[DAFilename] = deriveEncoder[DAFilename]
  implicit val filenameDecoder: Decoder[DAFilename] = deriveDecoder[DAFilename]
  implicit val documentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder[DocumentAttribute]
  implicit val documentAttributeDecoder: Decoder[DocumentAttribute] = deriveDecoder[DocumentAttribute]
}

如果您不限于选择JSON Parser/Serialializer,则可以使用jsoniter-scala尝试更有效的解决方案。

免责声明:我是这个库的作者。

这是两个实现的基准的结果:

[info] Benchmark                                        Mode  Cnt        Score           Error   Units
[info] ListOfAdtWithBase64Benchmark.readCirce           thrpt 5   114927.343 ±   7910.068 ops/s
[info] ListOfAdtWithBase64Benchmark.readJsoniterScala   thrpt 5  1818299.170 ± 162757.404 ops/s
[info] ListOfAdtWithBase64Benchmark.writeCirce          thrpt 5   117982.635 ±   8942.816 ops/s
[info] ListOfAdtWithBase64Benchmark.writeJsoniterScala  thrpt 5  4281752.461 ± 319953.287 ops/s

全部来源在这里。

最新更新