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
相关产品推荐
相关产品推荐

