Scala: декодировать массив Any [] с помощью circe.io

У меня есть сервер 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!"
  ]
}

Наличие Any обычно является признаком плохого дизайна и не поддерживается классом типов (например, кодеком).

cchantep 26.10.2018 17:28

Что ж, вся эта концепция имитирует Java public static String format(String format, Object... args).

Florian Baierl 26.10.2018 18:36

Любопытно, не могли бы вы опубликовать образец ввода json?

Biswanath 30.10.2018 14:51

@Biswanath Я добавил пример.

Florian Baierl 02.11.2018 09:04

Какие типы типов вы ожидаете от аргументов, например Int, string? Чего еще ты ждешь?

Biswanath 02.11.2018 09:37

Я бы сказал, примитивы и струны.

Florian Baierl 02.11.2018 10:40
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
6
1 023
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Предполагая, что ваши аргументы состоят из 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]]]]

Florian Baierl 02.11.2018 15:31
Ответ принят как подходящий

Добавляем его как еще один ответ, чтобы история оставалась чистой.

В основном следующий код просто работает с текущим значением 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))

Другие вопросы по теме