как передать общие типы в 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#.


person Christian Westman    schedule 11.06.2013    source источник
comment
Я предполагаю, что decodeOption возвращает Option, но ваш метод возвращает T   -  person Lukasz Madon    schedule 11.06.2013
comment
спасибо, лукас, забыл добавить .get в пример.   -  person Christian Westman    schedule 11.06.2013


Ответы (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": "[email protected]",
      "name": "foo bar"
    }"""

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

Что касается других фреймворков Json, вы можете изучить:

Воспроизведение Json

json4s

spray-json

Модуль Джексона Scala

person cmbaxter    schedule 11.06.2013

Кажется, что 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 уже определяет декодеры для многих стандартных типов, поэтому для этих типов вам не нужно делать ничего особенного. Для других типов (например, ваших пользовательских типов) вам придется определить декодеры и импортировать их.

person Régis Jean-Gilles    schedule 11.06.2013