3

I am trying to wrap Argonaut (http://argonaut.io) in order to serialize/deserialize JSON in a Scala project. We where using Jerkson before but as it has been discontinued we are looking for an alternative.

This is the basic JSON wrapper

import argonaut._, Argonaut._

object Json {
  def Parse[T](input: String): T = {
    input.decodeOption[T].get
  }
}

When I try and compile this I get the following errors.

could not find implicit value for evidence parameter of type argonaut.DecodeJson[T]
    input.decodeOption[T]
                  ^
not enough arguments for method decodeOption: (implicit evidence$6: argonaut.DecodeJson[T]) Option[T].
Unspecified value parameter evidence$6.
    input.decodeOption[T]
                  ^

Any suggestions on how to fix this or pointers on what I am doing wrong would be most appreciated.

Also suggestions on alternative JSON frameworks are very welcome.

I'm kind of new to Scala/Java and how generics work there but I have been writing .NET/C# for many years.

Christian Westman
  • 2,985
  • 1
  • 26
  • 28

2 Answers2

7

In order to make your code work, you will need to redefine the Json object like so:

object Json {
  def Parse[T](input: String)(implicit decode:DecodeJson[T]): Option[T] = {
    input.decodeOption[T]
  }
}

The thing you were missing was the implicit DecodeJson instance that the decodeOption function needs in order to figure out how to decode. You also need to define the return type as Option[T] instead of just T. A full example of this all working would look like this:

import argonaut._, Argonaut._
case class User(id:Long, email:String, name:String)

object Json {
  def Parse[T](input: String)(implicit decode:DecodeJson[T]): Option[T] = {
    input.decodeOption[T]
  }
}

object JsonTest{
  implicit val userDecode = casecodec3(User.apply, User.unapply)("id", "email", "name")

  def main(args: Array[String]) {
    val json = """{
      "id": 1,
      "email": "foo@test.com",
      "name": "foo bar"
    }"""

    val userOpt = Json.Parse[User](json)
    println(userOpt)
  }
}

As far as other Json frameworks, you could look into:

Play Json

json4s

spray-json

Jackson Scala Module

cmbaxter
  • 35,283
  • 4
  • 86
  • 95
4

It seems that Argonaut, like pretty much all scala serialization libraries, uses the type class pattern. This sounds like a fancy thing, but actually it just means that when serializing/deserializing an object of type T, it needs you to implicitly pass an instance of another object to which part or all of the process is deferred to. Specifically, when you do decodeOption[T], you need to have in scope an instance of argonaut.DecodeJson[T] (which decodeOption will use during the deserialization).

What you should do is simply to require this implicit value to be passed to Parse (it will then automatically be passed along to decodeOption:

def Parse[T](input: String)(implicit decoder: argonaut.DecodeJson[T]): Option[T] = {
  input.decodeOption[T]
}

Scala even provides some syntactic sugar to make the declaration shorter (this is called a "context bound"):

def Parse[T:argonaut.DecodeJson](input: String): Option[T] = {
  input.decodeOption[T]
}

Now, when calling Parse, you'll need to bring in scope an implicit value of argonaut.DecodeJson, or the call will fail to compile. Apparently the Argonaut object already defines decoders for many standard types, so for those types you won't have anything special to do. For other types (such as custom types of yours), you'll have to define decoders and import them.

Régis Jean-Gilles
  • 32,541
  • 5
  • 83
  • 97