Last active
February 27, 2022 09:31
-
-
Save odenzo/98efe3bda57ed95e8e5595730815aeca to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait CirceUtils { | |
val unCaptialize: String => String = s => | |
if s == null then null | |
else | |
s.headOption match { | |
case Some(v) if v.isUpper => s.drop(1).prepended(v.toLower) | |
case _ => s | |
} | |
val capitalize: String => String = (s: String) => s.capitalize | |
def mapKeys(mapping: Map[String, String])(s: String): String = mapping.get(s).fold(s)(n => n) | |
/** Make sure this is done eagerly, the inverse used for changing from Json Key Names to case class field names */ | |
def reverse(mapping: Map[String, String]): Map[String, String] = { | |
assert(mapping.values.toSet.size == mapping.size, "There should be no duplicate values in table") | |
mapping.toList.map(_.swap).to(Map) | |
} | |
/** Applies fn to all keys in the Json which should be a JsonObject */ | |
def transformKeys(fn: String => String)(obj: Json): Json = | |
obj.mapObject { o => JsonObject.fromIterable(o.toIterable.map((k, v) => fn(k) -> v)) } | |
def prepareKeys(fn: String => String)(cursor: ACursor): ACursor = | |
cursor.withFocus(transformKeys(fn)) | |
def encoderTransformKey(fn: String => String)(obj: JsonObject): JsonObject = | |
transformKeys(fn) | |
.compose(Json.fromJsonObject) | |
.andThen(json => json.asObject.get) | |
.apply(obj) | |
} | |
An Example Codec with upcase or downcase: | |
case class Foo(aaa: String, bbbb: Int, c: Boolean) | |
object Foo: | |
val decoder: Decoder[Foo] = deriveDecoder[Foo].prepare(prepareKeys(unCaptialize)) | |
val encoder: Encoder.AsObject[Foo] = deriveEncoder[Foo].mapJsonObject(encoderTransformKey(capitalize)) | |
val codec: Codec.AsObject[Foo] = Codec.AsObject.from(decoder, encoder) | |
And one to rename: | |
case class Bar(aaa: String, bbbb: Int, c: Boolean) | |
object Bar: | |
val rename: Map[String, String] = Map("aaa" -> "TheFirst", "bbbb" -> "TheSecond", "cIsNotMapped" -> "IsItTrue", "noSuchKey" -> "ERROR") | |
val decoder: Decoder[Bar] = deriveDecoder[Bar].prepare(prepareKeys(mapKeys(reverse(rename)))) | |
val encoder: Encoder.AsObject[Bar] = deriveEncoder[Bar].mapJsonObject(encoderTransformKey(mapKeys(rename))) | |
val codec: Codec.AsObject[Bar] = Codec.AsObject.from(decoder, encoder) | |
Note that not all fields need to be renamed, and the renaming Map can have extra fields w/o error. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment