У меня есть сервер Java, создающий сообщение, подобное этому:
@SerializedName("message")
private String _message;
@SerializedName("args")
private Object[] _args;
Теперь в моем приложении Scala.js я хочу десериализовать это сообщение, используя что-то вроде:
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)
}
}
Однако Scala отказывается декодировать это с ошибкой:
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
Есть идеи, как это расшифровать? Мне нужно только вызвать map(toString) о результате.
Редактировать
При попытке сделать что-то вроде этого:
args <- c.downField("args").as[Array[Any]].map(_.toString)
Я получаю следующую ошибку:
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
Редактировать 2
args <- c.downField("args").as[Seq[String]].map(_.toString)
Компилируется, но не разбирает файл json (слева).
Редактировать 3
Один пример отправленного json (в данном случае с целыми числами):
{
"message" : "{0} is smaller than {1}.",
"args" : [
1,
2
]
}
Java-сервер также может сгенерировать JSON следующего вида:
{
"message" : "{0} is smaller than {1}. ({2})",
"args" : [
1,
2,
"Hello World!"
]
}
Что ж, вся эта концепция имитирует Java public static String format(String format, Object... args).
Любопытно, не могли бы вы опубликовать образец ввода json?
@Biswanath Я добавил пример.
Какие типы типов вы ожидаете от аргументов, например Int, string? Чего еще ты ждешь?
Я бы сказал, примитивы и струны.




Предполагая, что ваши аргументы состоят из Int и String, посмотрите, работает ли это для вас
case class Notification(message: String, args: String*)
object Notification {
implicit val decodeIntOrString: Decoder[Either[Int, String]] =
Decoder[Int].map(Left(_)).or(Decoder[String].map(Right(_)))
implicit val messageDecoder: Decoder[Notification] = Decoder.instance(c => {
for {
message <- c.downField("message").as[String]
args <- c.downField("args").as[List[Either[Int,String]]].map(_.toString)
} yield {
Notification(message, args)
}
})
}
Это был тестовый пример
val json = """
{
"message" : "Hello" ,
"args" : [ 2, 3, 4,"good" ]
}
"""
println(decode[Notification](json))
Также я хотел бы указать на обсуждение окружности https://github.com/circe/circe/issues/216, в котором говорится о проблеме более или менее так же. Я думаю, что идиоматическое решение Circe похоже на то, что типы будут выводить десериализацию.
Я предполагаю, что это сработает для Ints и Strings, но мне пришлось бы делать больше этих функций декодирования Either для чисел с плавающей запятой, bools и т. д., Которые были бы очень уродливыми. (что-то вроде: decodeIntOrStringOrBoolOrFloat: Decoder[Either[Int, Either[String, Either[Boolean, Float]]]]
Добавляем его как еще один ответ, чтобы история оставалась чистой.
В основном следующий код просто работает с текущим значением json и использует его строковое представление.
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)
}
})
}
И тестовый пример
val json = """
{
"message" : "Hello" ,
"args" : [ 2, 3.234, 4,"good", true ]
}
"""
println(decode[Notification](json))
Наличие
Anyобычно является признаком плохого дизайна и не поддерживается классом типов (например, кодеком).