如何在 circe 中覆盖默认编解码器?
How do I override default codec in circe?
我想将我案例 类 的 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)
}
}
@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/serializer 的选择上不受限制,那么您可以使用 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
完整来源为 here。
当你这样做时:
implicit val DocumentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder
circe
尝试为 DAFilename
和 DAAudio
找到合适的 Encoder
。但是,由于那些已经存在(通过 @JsonCodec
在个人 类 上),它不会 re-derive 使用泛型和你的范围内的 Encoder[Array[Byte]]
从头开始 re-derive 它们 - 这是你想要的.
所以你可以摆脱 @JsonCodec
(所以它 auto-derives 编解码器 DAFilename
和 DAAudio
连同 DocumentAttribute
)或触发 re-derivation 手动:
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
重复上述过程,否则它将尝试将 Base64 字符串解析为整数列表,结果失败了。
我想将我案例 类 的 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)
}
}
@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/serializer 的选择上不受限制,那么您可以使用 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
完整来源为 here。
当你这样做时:
implicit val DocumentAttributeEncoder: Encoder[DocumentAttribute] = deriveEncoder
circe
尝试为 DAFilename
和 DAAudio
找到合适的 Encoder
。但是,由于那些已经存在(通过 @JsonCodec
在个人 类 上),它不会 re-derive 使用泛型和你的范围内的 Encoder[Array[Byte]]
从头开始 re-derive 它们 - 这是你想要的.
所以你可以摆脱 @JsonCodec
(所以它 auto-derives 编解码器 DAFilename
和 DAAudio
连同 DocumentAttribute
)或触发 re-derivation 手动:
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
重复上述过程,否则它将尝试将 Base64 字符串解析为整数列表,结果失败了。