5

I'm create a number of json messages for spray in scala using case classes. For example:

  case class Foo(name: String, attrs: List[String])
  implicit val fooFormat = jsonFormat2(Foo)
  object Foo {
    case class Invalid(error: String)
  }
  case class Bar(name: String, kv: Map[String, String])
  implicit val barFormat = jsonFormat2(Bar)

In the above snippet, barFormat compiles, but fooFormat does not:

type mismatch; found : Foo.type required: (?, ?) => ? 
 Note: implicit value barFormat is not applicable here because it comes 
 after the application point and it lacks an explicit result type

I don't want to use barFormat in place of fooFormat, and I understand that a case class automatically generates a companion object, but I don't understand why there's a compiler error here, and the error message is difficult for me to decipher. Does anyone know what the problem is here and how to fix it, preferably without removing my Foo companion object?

jonderry
  • 21,385
  • 29
  • 93
  • 160
  • https://issues.scala-lang.org/browse/SI-801 – Mik378 Jun 17 '14 at 21:46
  • At least give us the type of `jsonFormat2`. – wingedsubmariner Jun 17 '14 at 22:30
  • 1
    @downvoter, please explain your downvote. – jonderry Jun 17 '14 at 22:44
  • @wingedsubmariner, the type of `jsonFormat2` is fairly cryptic: `def jsonFormat2[P1, P2, T T)(implicit evidence$4: spray.json.DefaultJsonProtocol.JF[P1],implicit evidence$5: spray.json.DefaultJsonProtocol.JF[P2],implicit evidence$6: ClassManifest[T]): spray.json.RootJsonFormat[T]` – jonderry Jun 17 '14 at 22:46
  • Sorry, I didn't recognize that was part of spray, I thought it was a method you wrote. Actually, its type is `def jsonFormat2[A :JF, B :JF, T <: :classmanifest="" b="" product=""> T): RootJsonFormat[T]` - you probably made a copy/paste mistake and removed the `construct`. – wingedsubmariner Jun 17 '14 at 23:12
  • Is the "right" solution then to pass `apply` directly? I was just copying an example of serializing case classes when I wrote the initial format, and it did not include `apply`, though it works fine until you "override" the case class's companion object. I guess this obscures the passage of the `apply` method by simply providing the case class name. Is it bad practice to define a case class companion object? – jonderry Jun 17 '14 at 23:32
  • @jonderry Faced this same problem. You might want to change the 'spray' tag to 'spray-json'. The two projects are related (same author), but spray.json is not part of spray, I think. – akauppi Jun 30 '15 at 09:45

3 Answers3

9

From your compile error, it looks like jsonFormat2 expects a two-argument function. Do you mean to pass the constructors of Foo and Bar into it? If so, you should do Foo.apply and Bar.apply.

geoffliu
  • 500
  • 2
  • 10
  • It's a nuisance agreed, but there's no better workaround right now. – jrudolph Jun 20 '14 at 13:25
  • I don't understand this answer. The `jsonFormat2`'s number is supposed to match the number of constructor parameters for the case class (which it does). See [here](https://github.com/spray/spray-json#providing-jsonformats-for-case-classes) for Spray.json sample. Did @jonderry get the problem solved? – akauppi Jun 30 '15 at 09:51
  • @akauppi, what's the problem with this answer? I'm suggesting exactly what you answered below, using `Foo.apply` – geoffliu Jul 01 '15 at 15:32
  • Ah, you're right. Before knowing the right answer, I thought you meant passing both `Foo` and `Bar` (or `Foo.apply` and `Bar.apply`) to (maybe both) the constructors, since they're expecting two arguments. Your text is correct - it says "two argument function" (different thing). Maybe because it doesn't actually show the correct way, I failed to recognize the solution was indeed right. Maybe rephrase or add the code clip from my answer? – akauppi Jul 02 '15 at 07:31
  • There actually exists `jsonFormat1` through `jsonFormat22`. It matches allowed constructor params for case classes. To make it more automatic, I think, would require macros. – sfosdal Aug 02 '17 at 00:49
4

Case class companion objects will by default extend one of the function traits. object Foo would have extended ((String, List[String]) => Foo) but when you manually defined it you didn't extend that trait. This is why you couldn't pass it to jsonFormat2, which was expecting a (?, ?) => ?. If you make the following change your code should compile:

object Foo extends ((String, List[String]) => Foo) {
wingedsubmariner
  • 12,460
  • 1
  • 23
  • 45
1

Spray.json documentation suggests an easier way than @wingedsubmariner's suggestion:

If you explicitly declare the companion object for your case class the notation above will stop working. You'll have to explicitly refer to the companion object's apply method to fix this:

So the correction for the question becomes:

implicit val fooFormat = jsonFormat2(Foo.apply)

Added: This is in fact what also @geoffliu suggests in his answer.

akauppi
  • 14,244
  • 12
  • 73
  • 94