如何将泛型类型传递给Argonaut

时间:2013-06-11 11:15:18

标签: scala jerkson

我正在尝试包装Argonaut(http://argonaut.io)以便在Scala项目中序列化/反序列化JSON。我们之前使用过Jerkson但是因为它已经停产,我们正在寻找替代方案。

这是基本的JSON包装器

import argonaut._, Argonaut._

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

当我尝试编译时,我得到以下错误。

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]
                  ^

关于如何解决这个问题的建议或关于我做错的指示将是最受欢迎的。

非常欢迎有关替代JSON框架的建议。

我不熟悉Scala / Java以及泛型如何在那里工作但我多年来一直在编写.NET / C#。

2 个答案:

答案 0 :(得分:7)

为了使您的代码有效,您需要重新定义Json对象,如下所示:

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

您缺少的是DecodeJson函数需要的隐式decodeOption实例,以便弄清楚如何解码。您还需要将返回类型定义为Option[T],而不仅仅是T。所有工作的完整示例如下所示:

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

就其他Json框架而言,您可以查看:

Play Json

json4s

spray-json

Jackson Scala Module

答案 1 :(得分:4)

似乎Argonaut与几乎所有scala序列化库一样,使用类型类模式。这听起来很奇特,但实际上它只是意味着在序列化/反序列化类型为T的对象时,它需要隐式传递另一个对象的实例,该对象的部分或全部延迟到该对象。 具体来说,当您执行decodeOption[T]时,您需要在范围内拥有argonaut.DecodeJson[T]的实例(decodeOption将在反序列化期间使用)。

你应该做的只是要求将这个隐含值传递给Parse(然后它会自动传递给decodeOption

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

Scala甚至提供了一些语法糖来使声明更短(这称为“上下文绑定”):

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

现在,在调用Parse时,您需要为范围引入隐式值argonaut.DecodeJson,否则调用将无法编译。显然,Argonaut对象已经为许多标准类型定义了解码器,因此对于这些类型,您将没有任何特殊要做。 对于其他类型(例如您的自定义类型),您必须定义解码器并导入它们。