3

我想将Array[Byte]我的案例类的字段编码为 Base64 字符串。出于某种原因,Circe 没有使用默认的编解码器来选择我的编解码器,而是将字节数组转换为整数的 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)
  }
}
4

2 回答 2

1

当你这样做时:

implicit val DocumentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder

circe试图变得Encoder适合DAFilenameand DAAudio。但是,由于这些已经存在(通过@JsonCodec单个类),它不会使用泛型和你Encoder[Array[Byte]]的范围从头开始重新派生它们 - 你想要的。

因此,您可以摆脱@JsonCodec(因此它自动派生编解码器DAFilename并与DAAudio一起DocumentAttribute)或手动触发重新派生:

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

你还需要构建一个DecoderforArray[Byte]并重复上面的过程for Decoders,否则它会尝试将Base64字符串解析为int列表,导致失败。

于 2018-03-01T13:20:51.137 回答
0

它表明@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 解析器/序列化器的选择没有限制,那么您可以尝试使用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

完整的来源在这里

于 2018-03-01T13:14:02.567 回答