【问题标题】:Implicit jsonFormat for case class with varargs带有可变参数的案例类的隐式 jsonFormat
【发布时间】:2015-03-28 12:33:36
【问题描述】:

我有一个包含可变参数的案例类,其隐式 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?

【问题讨论】:

  • 您是否已经考虑过使用 Seq[String] 代替?
  • 使用List[String] 代替可变参数,或者制作自己的解组器。

标签: scala akka spray spray-json


【解决方案1】:

我也遇到过这个异常,但在我的情况下,这是由于案例类中的“val”定义(不是作为传递的参数)。

有问题的案例类:

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

工作案例类:

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

不幸的是,现在每次调用都会计算“代码”。

(使用 Akka Http 版本 - 10.1.1)

【讨论】:

    【解决方案2】:

    它应该可以完美运行,可能你在隐式中有一些歧义。这非常有效:

    import spray.json._, DefaultJsonProtocol._
    
    case class Test(param: String*)
    object Test {
      implicit val testFormat = jsonFormat1(Test.apply)
    }
    

    就像一个最佳实践建议一样,不要使用协议模式,它会导致大型项目中的大量隐式错误,请始终在伴随对象中定义隐式(因为存在异常情况)。另一点避免继承,它并不是真正需要的。

    Scala * 模式只是 Seq 类型构造函数的糖,所以它应该为这种情况找到 seqFormat (un)marshaller。

    更新

    这不起作用,因为 Spray 使用 ClassManifestcopy 函数中提取字段名称,但编译器不会为构造函数中带有可变参数的案例类生成此函数:

    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())
    

    因此您需要手动提供字段名称。顺便说一句,我以前不知道这个

    【讨论】:

    • 在 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
    • @mirelon 你需要切换到“:paste”模式然后评估它
    • 现在出现错误:java.lang.RuntimeException: Cannot automatically determine case class field names and order for 'Test', please use the 'jsonFormat' overload with explicit field name specificationCaused by: java.lang.RuntimeException: Case class Test declares additional fields(注意我在你的代码中添加了一行 Test("a","b").toJson
    • @mirelon 更改为 jsonFormat(Test.apply _, "param")
    • 谢谢,现在可以了,但我不明白为什么。你能用解释编辑答案吗?
    【解决方案3】:

    我建议您使用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")
    

    【讨论】:

    • 当我添加implicit val colorsFormat = jsonFormat1(Colors.apply) 时出现错误ambiguous reference to overloaded definition,当我使用implicit val colorsFormat = jsonFormat1(Colors) 时出现错误type mismatch; found : Colors.type required: ? => ?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-02
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    • 2015-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多