I have a Java server creating a message looking like this:
@SerializedName("message")
private String _message;
@SerializedName("args")
private Object[] _args;
Now in my Scala.js application, I want to deserialize this message using something like:
case class Notification(message: String, args: String*)
implicit val messageDecoder: Decoder[Notification] = (c: HCursor) => {
for {
message <- c.downField("message").as[String]
args <- c.downField("args").as[List[java.lang.Object]].map(_.toString)
} yield {
Notification(level, message, args)
}
}
However, Scala refuses to decode this with the error:
implicit error;
[error] !I d: Decoder[List[Object]]
[error] Decoder.importedDecoder invalid because
[error] !I exported: Exported[Decoder[List[Object]]]
[error] Decoder.decodeCanBuildFrom invalid because
[error] !I d: Decoder[Object]
[error] ??Decoder.importedDecoder invalid because
[error] !I exported: Exported[Decoder[Object]]
[error]
[error] Decoder.decodeList invalid because
[error] !I evidence$2: Decoder[Object]
[error] ??Decoder.importedDecoder invalid because
[error] !I exported: Exported[Decoder[Object]]
[error] args <-
c.downField("args").as[List[Object]].map(_.toString)
[error] ^
[error] one error found
Any ideas on how to decode this? I only need to call map(toString)
on the result.
Edit
When trying to do something like this:
args <- c.downField("args").as[Array[Any]].map(_.toString)
I get the following error:
diverging implicit expansion for type io.circe.Decoder[A]
[error] starting with value decodeString in object Decoder
[error] args <-
c.downField("args").as[Array[Any]].map(_.toString)
[error] ^
[error] one error found
Edit 2
args <- c.downField("args").as[Seq[String]].map(_.toString)
Does compile, but does not succefully parse the json (Left).
Edit 3
One example of the json that is sent (in this case with integers):
{
"message" : "{0} is smaller than {1}.",
"args" : [
1,
2
]
}
The java server could also generate a JSON looking like this:
{
"message" : "{0} is smaller than {1}. ({2})",
"args" : [
1,
2,
"Hello World!"
]
}
Adding it as another answer, so as to keep the history clean.
Basically the following code simply works with the current json value and uses it string representation.
case class Notification(message: String, args: String*)
object Notification {
implicit val anyDecoder : Decoder[Any] = Decoder.instance(c => {
c.focus match {
case Some(x) => Right(x)
case None => Left(DecodingFailure("Could not parse", List()))
}
})
implicit val messageDecoder: Decoder[Notification] = Decoder.instance(c => {
for {
message <- c.downField("message").as[String]
args <- c.downField("args").as[List[Any]].map(_.toString)
} yield {
Notification(message, args)
}
})
}
And the test case is
val json = """
{
"message" : "Hello" ,
"args" : [ 2, 3.234, 4,"good", true ]
}
"""
println(decode[Notification](json))