You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jackson PropertyNamingStrategy的Circe替代方案咨询

Circe实现蛇形命名与样例类映射的方案

从Jackson迁移到Circe后,要实现蛇形命名(SnakeCase)的JSON与Scala样例类的自动映射,最便捷的方式是借助circe-generic-extras模块的内置命名策略,以下是具体步骤:

方法一:使用circe-generic-extras(推荐)

这是最接近Jackson注解方式的实现,无需手动编写大量编码解码逻辑。

1. 添加依赖

在你的构建文件中加入circe-generic-extras依赖(以SBT为例,版本可根据项目实际情况调整):

libraryDependencies += "io.circe" %% "circe-generic-extras" % "0.14.6"

2. 配置命名策略并派生Codec

可以通过全局隐式配置或样例类伴生对象配置来指定蛇形命名规则:

全局配置方式

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

// 全局隐式配置:启用蛇形命名映射
implicit val snakeCaseConfig: Configuration = Configuration.default.withSnakeCaseMemberNames

// 定义样例类(注意修正原示例中firstName的类型错误,JSON字符串对应Option[String]而非Option[Boolean])
final case class User(
    firstName: Option[String] = None,
    lastName: Option[String] = None
)

// 自动派生User的Codec
implicit val userCodec: Codec[User] = deriveCodec[User]

伴生对象注解方式

如果想将配置与样例类绑定,可使用@ConfiguredJsonCodec注解:

import io.circe.generic.extras.ConfiguredJsonCodec
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto.deriveCodec

@ConfiguredJsonCodec
final case class User(
    firstName: Option[String] = None,
    lastName: Option[String] = None
)

// 在伴生对象中提供蛇形命名配置
object User {
  implicit val config: Configuration = Configuration.default.withSnakeCaseMemberNames
}

3. 测试解析与序列化

import io.circe.parser.decode
import io.circe.syntax._

// 解析外部服务返回的蛇形JSON
val jsonStr = """{"first_name": "John", "last_name": "Doe"}"""
val decodedUser = decode[User](jsonStr)
// 结果为 Right(User(Some("John"), Some("Doe")))

// 将样例类序列化为蛇形JSON
val user = User(Some("Jane"), Some("Smith"))
val json = user.asJson
// 输出 {"first_name":"Jane","last_name":"Smith"}

方法二:手动编写编码器/解码器

如果不想依赖circe-generic-extras模块,也可以手动实现编解码逻辑:

import io.circe.{Decoder, Encoder, HCursor, Json}
import io.circe.parser.decode
import io.circe.syntax._

final case class User(
    firstName: Option[String] = None,
    lastName: Option[String] = None
)

// 手动实现解码器
implicit val userDecoder: Decoder[User] = (c: HCursor) =>
  for {
    firstName <- c.downField("first_name").as[Option[String]]
    lastName <- c.downField("last_name").as[Option[String]]
  } yield User(firstName, lastName)

// 手动实现编码器
implicit val userEncoder: Encoder[User] = (user: User) =>
  Json.obj(
    "first_name" -> user.firstName.asJson,
    "last_name" -> user.lastName.asJson
  )

// 测试逻辑和方法一一致
val decoded = decode[User]("""{"first_name": "John", "last_name": "Doe"}""")
val encoded = User(Some("Jane"), Some("Smith")).asJson

注意:原示例中firstName的类型定义为Option[Boolean],但对应JSON字段是字符串值,这会导致解析失败,建议修正为Option[String]。

内容的提问来源于stack exchange,提问作者vukojevicf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 06:35:30