Раствор 1
Circe получает имена полей из вашего экземпляра класса case и проходит через JSON с помощью курсора, пытается получить значение каждого имени поля и пытается преобразовать его в желаемый тип.
Это означает, что ваш декодер не сможет обрабатывать оба случая.
Решение этой проблемы - написать два декодера:
- Базовый декодер (производная Encoder будет работать)
- Кодер, который использует HCursor для навигации по вашему JSON и получения ключей регистра змей
val decoderDerived: Decoder[Camel] = deriveDecoder
val decoderCamelSnake: Decoder[Camel] = (c: HCursor) =>
for {
firstName <- c.downField("first_name").as[String]
lastName <- c.downField("last_name").as[String]
waterPerDay <- c.downField("water_per_day").as[Int]
} yield {
Camel(firstName, lastName, waterPerDay)
}
Затем вы можете объединить эти два декодера в один, используя Decoder # или
implicit val decoder: Decode[Camel] = decoderDerived or decoderCamelSnake
Decoder # или попытается декодировать, используя первый декодер, и если он потерпит неудачу, то попробует второй.
Решение 2
Если вам подходит только ввод данных camel_case, вы можете использовать @ConfiguredJsonCodec
из пакета "io.circe" %% "circe-generic-extras" % circeVersion
. Обратите внимание, что для использования этой аннотации вам также необходимо включить плагин компилятора Paradise.
addCompilerPlugin(
"org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full
)
@ConfiguredJsonCodec
case class User(
firstName: String,
lastName: String
)
object User {
implicit val customConfig: Configuration = Configuration.default.withSnakeCaseMemberNames
}
val userJson = User("John", "Doe").asJson
println(userJson)
// { "first_name" : "John", "last_name" : "Doe" }
val decodedUser = decode[User](userJson.toString)
println(decodedUser)
// Right(User("John", "Doe"))
Также обратите внимание, что вам не нужно писать собственные производные декодера и кодировщика, поскольку эта конфигурация сделает это за вас.