Closed hohonuuli closed 9 months ago
As part of this, I'm hoping to support both the original, legacy snake_case
and the more modern camelCase
. I'm using a pattern that tries to decode camelCase first then falls back to snake_case. To do this, I just create a snake case and camel case version of the same case classes. Each case class has a method to convert to the other type (e.g. toSnakeCase
or toCamelCase
.) Then add codecs like:
given associationScDecoder: Decoder[AssociationSC] = deriveDecoder
given associationScEncoder: Encoder[AssociationSC] = deriveEncoder
private val associationCcDecoder: Decoder[Association] = deriveDecoder
given associationEncoder: Encoder[Association] = deriveEncoder
given associationDecoder: Decoder[Association] = associationCcDecoder or associationScDecoder.map(_.toCamelCase)
For this to work, the snake case and camel case need to have a required field not found in the other.
Need to deal with snake_case/camelCase divide. Tip from Stackoverflow:
Circe gets field names from your case class instance and traverses through JSON using cursor, tries to get the value of each field name and tries to convert it to your desirable type.
It means that your decoder won't be able to process both cases.
The solution to this problem is to write two decoders:
Basic decoder (deriveEncoder will work) Encoder which uses HCursor to navigate through your JSON and get snake case keys
Then you can combine these two decoders into one using Decoder#or
Decoder#or will try to decode using first decoder, and if it fails, then it will try out the second one.