Given incoming json like below, how can i decode it the given case class based on condition.
Incoming JSON
{
"config": {
"files": ["welcome"],
"channel": "media"
}
}
Case Classes
case class File(`type`: String, value: String)
case class Config(files: List[File],
channel: String = "BC")
object Config{
implicit val FileDecoder: Decoder[File] = deriveDecoder[File]
implicit val ConfigDecoder: Decoder[Config] = deriveDecoder[Config]
}
case class Inventory(config: Config)
object Inventory {
implicit val InventoryDecoder: Decoder[Inventory] = deriveDecoder[Inventory]
}
I do not have control on incoming files values in json it can be List[String] or List[File] so i need to handle both cases in my decoding logic.
So as we can see above my aim is to check if the incomes files values is List[String] then transform that values to below where type is hardcoded to "audio"
"files": [{
"type": "audio",
"value": "welcome.mp3"
}],
The overall json should look like below before it mapped into case classes for auto decoding.
{
"config": {
"files": [{
"type": "audio",
"value": "welcome.mp3"
}],
"channel": "media"
}
}
what i understood that this can be achieved either by transforming the json before decoding or can also be achieved during the decoding of files.
I tried writing the decoding logic at File level but i am not able to succeed. I am not getting the crux of how to do this.
Tried code
implicit val FileDecoder: Decoder[File] = deriveDecoder[File].prepare { (aCursor: ACursor) =>
{
if(!aCursor.values.contains("type")){
aCursor.values.map( v =>
Json.arr(
Json.fromFields(
Seq(
("type", Json.fromString("audio")),
("value", v.head)
)
)
)
)
}
}
}
CodePudding user response:
We can use a custom Decoder
for File
to provide a default value to type
final case class File(`type`: String, value: String)
object File {
implicit final val FileDecoder: Decoder[File] =
Decoder.instance { cursor =>
(
cursor.getOrElse[String](k = "type")(fallback = "audio"),
cursor.get[String](k = "value")
).mapN(File.apply)
}.or(
Decoder[String].map(value => File(`type` = "audio", value))
)
}
Which can be used like this:
val data =
"""[
{
"type": "audio",
"value": "welcome.mp3"
},
{
"value": "foo.mp3"
},
"bar.mp3"
]"""
parser.decode[List[File]](data)
// res: Either[io.circe.Error, List[File]] =
// Right(List(
// File("audio", "welcome.mp3"),
// File("audio", "foo.mp3"),
// File("audio", "bar.mp3")
// ))
You can see the code running here.