Неявный jsonFormat для класса case с varargs

У меня есть класс case, содержащий varargs, с неявным jsonFormat следующим образом:

import spray.json._
case class Colors(name: String*)
object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat1(Colors)
}
import MyJsonProtocol._
Colors("CadetBlue").toJson

Выдает ошибку:

error: type mismatch;
found   : Color2.type
required: Seq[String] => Color2
Note: implicit value colorFormat is not applicable here because it comes after the application point and it lacks an explicit result type
      implicit val colorFormat = jsonFormat1(Color2)
                                            ^

Я также пробовал:

implicit val colorFormat = jsonFormat1(Colors.apply)

что вызвало другое (время выполнения!) исключение:

java.lang.RuntimeException: Cannot automatically determine case class field names and order for 'Colors', please use the 'jsonFormat' overload with explicit field name specification

Последующий:

implicit val colorFormat = jsonFormat(Colors, "name")

вызывает прежнюю ошибку

Можно даже определить неявный jsonFormat для класса case с varargs?


person mirelon    schedule 29.01.2015    source источник
comment
Вы уже рассматривали возможность использования вместо этого Seq[String]?   -  person mavilein    schedule 29.01.2015
comment
Используйте List[String] вместо varargs или создайте свой собственный демаршаллер.   -  person Mariusz Nosiński    schedule 29.01.2015


Ответы (3)


Это должно работать отлично, возможно, у вас есть некоторая двусмысленность в имплицитах. Это отлично работает:

import spray.json._, DefaultJsonProtocol._

case class Test(param: String*)
object Test {
  implicit val testFormat = jsonFormat1(Test.apply)
}

Как совет передовой практики, не используйте шаблон протокола, это приводит к большим неявным ошибкам в большом проекте, всегда определяйте неявные в вашем сопутствующем объекте (потому что есть случаи исключения). Еще один момент, избегайте наследования, это на самом деле не нужно.

Шаблон Scala * - это просто сахар для конструктора типа Seq, поэтому он должен найти seqFormat (не)маршаллер для этих случаев.

Обновить

Это не работает, потому что Spray использует ClassManifest для извлечения имен полей из функции copy, но компилятор не генерирует эту функцию для классов case с varargs в конструкторе:

case class Test(params: String*)
def man[A: ClassManifest] = implicitly[ClassManifest[A]]
man[Test].erasure.getDeclaredMethods.filter(_.getName.contains("copy"))
res4: Array[java.lang.reflect.Method] = Array()

scala> case class Test(param: String)
defined class Test

scala> man[Test].erasure.getDeclaredMethods.filter(_.getName.contains("copy"))
warning: there was one deprecation warning; re-run with -deprecation for details
res5: Array[java.lang.reflect.Method] = Array(public Test Test.copy(java.lang.String), public java.lang.String Test.copy$default$1())

Поэтому вам нужно указать имя поля вручную. Кстати, я не знал этого раньше

person 4lex1v    schedule 30.01.2015
comment
Пробовал в консоли scala: error: value apply is not a member of object Test \n Note: implicit value testFormat is not applicable here because it comes after the application point and it lacks an explicit result type - person mirelon; 30.01.2015
comment
@mirelon вам нужно переключиться в режим :paste, а затем оценить его - person 4lex1v; 30.01.2015
comment
Теперь ошибка: java.lang.RuntimeException: Cannot automatically determine case class field names and order for 'Test', please use the 'jsonFormat' overload with explicit field name specification и Caused by: java.lang.RuntimeException: Case class Test declares additional fields (Обратите внимание, я добавил строку с Test("a","b").toJson в ваш код) - person mirelon; 30.01.2015
comment
@mirelon изменить на jsonFormat(Test.apply _, "param") - person 4lex1v; 30.01.2015
comment
Спасибо, теперь это работает, но я не понимаю, почему именно. Не могли бы вы отредактировать ответ с объяснением? - person mirelon; 30.01.2015

Я также столкнулся с этим исключением, однако в моем случае это было связано с определением «val» в классе case (а не как переданный аргумент).

Класс проблемных обращений:

case class Color(name: String) {
  val code: Int = ...
}

Класс рабочих случаев:

case class Color(name: String) {
  def code: Int = ...
}

Что довольно неудачно, так как теперь «код» будет вычисляться при каждом вызове.

(Используется версия Akka Http - 10.1.1)

person Hanan Oanunu    schedule 05.09.2018

Я предлагаю вам определить класс case с помощью name: List[String] и создать объект-компаньон с настраиваемой функцией применения. Вы по-прежнему можете создавать такие объекты, как Colors("blue", "green").

case class Colors(name: List[String])

object Colors {
  def apply(name: String*): Colors = new Colors(name.toList)
  def apply(names: List[String]): Colors = new Colors(names)
}


Colors("blue", "green")
person Mustafa Simav    schedule 30.01.2015
comment
Когда я добавляю implicit val colorsFormat = jsonFormat1(Colors.apply), возникает ошибка ambiguous reference to overloaded definition, а когда я использую implicit val colorsFormat = jsonFormat1(Colors), возникает ошибка type mismatch; found : Colors.type required: ? => ? - person mirelon; 30.01.2015