circe / circe

Yet another JSON library for Scala

Home Page:https://circe.github.io/circe/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How do I map a particular key and value in a custom way?

githorse opened this issue · comments

Say I have a case class Cat(name: String, age: Int, colorKey: Int), and in .toJson I want to turn colorKey into a string by looking it up in a dictionary, something like (Map(1 -> "black", 2 -> "white", 3 -> "orange"):

case class Cat(name: String, age: Int, colorKey: Int)

val colorKeyToString = Map(1 -> "black", 2 -> "white", 3 -> "orange")
val colorStringToKey = Map("black" -> 1, "white" -> 2, "orange" -> 3)

val cat: Cat = Cat('Sophie', 16, 1)

val json = foo.asJson.noSpaces
println(json)
// '{"name": "Sophie", "age": 16, "color": "black" }

decode[Cat]('{"name": "Sophie", "age": 16, "color": "black" }')
// Cat('Sophie', 16, 1) 

(This specific example of looking up a key in a dictionary is not important; I'm just trying to motivate transforming a single key.)

I see how to create custom encoders/decoders, but it seems like that is intended to apply to all values of a particular type. Here I want to have specific control over the {to,from}Json on a particular field (colorKey), mapping both key and value, but ideally use the defaults for all the other fields (and not change the encoding for the Int type).

What's the best way to achieve that (for both asJson and decode)?

creating a custom encoder/decoder would be the way to go.

case class Cat(name: String, age: Int, colorKey: Int)
object Cat {
  val colorKeyToString = Map(1 -> "black", 2 -> "white", 3 -> "orange")
  val colorStringToKey = Map("black" -> 1, "white" -> 2, "orange" -> 3)

  implicit val encoder: Encoder[Cat] = Encoder.instance{ cat =>
    Json.obj(
      "name" := cat.name, 
      "age" := cat.age,
      "color" := colorKeyToString(cat.colorKey)
    )
  }

  implicit val decoder: Decoder[Cat] = Decoer.instance{ cursor =>
    for {
      name <- cursor.get[String]("name")
      age <- cursor.get[Int]("age")
      color <- cursor.get[String]("color").map(colorStringToKey)
    } yield Cat(name, age, color)
  }
}

calling the colorKeyToString in this manner is not really safe, since clients may supply an unknown value. It will result in a MatchException if that is case

You can mitigate that by using flatMap and converting the color in the last step, I leave this up to you.