Как передать универсальные типы Argonaut
Я пытаюсь обернуть Argonaut ( http://argonaut.io/), чтобы сериализовать / десериализовать JSON в проекте Scala. Мы раньше использовали 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 ответа
Чтобы ваш код работал, вам нужно переопределить 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, вы можете посмотреть на:
Похоже, что 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
Объект уже определяет декодеры для многих стандартных типов, поэтому для этих типов вам не нужно делать ничего особенного. Для других типов (например, пользовательских типов) вам придется определить декодеры и импортировать их.