Шаблон класса типа Scala и универсальные методы

Я пытаюсь написать универсальный экстрактор для разбора тела json POST с использованием спрея и спрея-json.

Однако я изо всех сил пытаюсь заставить его работать с более чем одной моделью. Вот оператор case в сервисном объекте:

import MyJsonProtocol._

...

def receive = {
  case Post (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p))
  case Get  (Routes.foo.forId(x))           => sender ! Ok(x)
  case _                                    => sender ! Ok("No handler")
}

А вот экстрактор, который я написал (работает, пока есть только JsonReader для одной модели в области оператора case):

//NB. Json.parse returns an Option[T]
object Post extends Request {
  def unapply[T:JsonReader](req: HttpRequest): Option[(String, T)] = req match {
    case HttpRequest(POST, url, _, HttpBody(_, body), _) => Json.parse[T](body.asString).map((url, _))
    case _ => None
  }
}

Однако, как только я добавляю новую модель (и связанный с ней JsonReader), код больше не компилируется с этой ошибкой:

ambiguous implicit values:
[error]  both value personFormat in object Json of type => spray.json.RootJsonFormat[com.rsslldnphy.foam.models.Person]
[error]  and value animalFormat in object Json of type => spray.json.RootJsonFormat[com.rsslldnphy.foam.models.Animal]
[error]  match expected type spray.json.JsonReader[T]
[error]     case Post (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p))

Тот факт, что общие типы JsonReaders различны, кажется, утерян. Является ли этот тип стиранием? Есть ли способ обойти это, чтобы получить то, что я хочу?

Вот полный компилируемый код проекта с комментарием в ExampleService, который объясняет, почему он ломается: github.com/ rsslldnphy/пена. Ваша помощь приветствуется, спасибо.

Или, если то, что я хочу, в настоящее время невозможно, может ли кто-нибудь предложить альтернативный подход?


person Russell    schedule 13.11.2012    source источник
comment
ты пробовал case Post[Person] ?   -  person Kim Stebel    schedule 13.11.2012
comment
Я получаю: not found: type Post. Я не определяю класс, просто объект.   -  person Russell    schedule 13.11.2012
comment
возможно, если вы хотите, чтобы в ответе был компилируемый код, вы должны указать то же самое в вопросе   -  person Kim Stebel    schedule 14.11.2012
comment
Я не просил компилируемый код, просто объяснение того, что вы имели в виду и почему это должно работать! Но если это поможет, я выложу весь проект на github.   -  person Russell    schedule 14.11.2012
comment
@KimStebel Я добавил к вопросу ссылку на github. Спасибо за ваше время на это.   -  person Russell    schedule 14.11.2012


Ответы (1)


Вы должны дать компилятору явные инструкции, чтобы заставить это работать. Как вы можете видеть ниже, компилятор никак не может определить, каким должен быть T. Компилятор должен иметь возможность динамически просматривать Json из запроса и из этого подразумевать тип (о чем мы можем только мечтать;))

def unapply[T:JsonReader](req: HttpRequest): Option[(String, T)] = (...) Json.parse[T] (...)

Это означает, что для выполнения этой работы вы должны явно аннотировать сообщение, как показано ниже:

import MyJsonProtocol._

...

def receive = {
  case Post[Person] (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p))
  case Get  (Routes.foo.forId(x))           => sender ! Ok(x)
  case _                                    => sender ! Ok("No handler")
}

и изменить определение на это

case class Post[T: JsonReader] extends Request {
  def unapply(req: HttpRequest): Option[(String, T)] = req match {
    case HttpRequest(POST, url, _, HttpBody(_, body), _) => Json.parse[T](body.asString).map((url, _))
    case _ => None
  }
}
person Reuben Doetsch    schedule 13.11.2012
comment
(Кроме того, не должен ли компилятор сделать вывод о типе T из того факта, что я явно называю его p: Person? Или из того факта, что метод, в который он передается, Actons.person.post, явно требует Person в качестве аргумента?) - person Russell; 14.11.2012
comment
Нет, вывод scala не такой мощный и не будет делать вывод из того, что вы вернете. Также unapply — странный случай — одна секунда работает над тем, как сделать unapply параметризованным. - person Reuben Doetsch; 14.11.2012
comment
Спасибо, но все равно не компилируется: [error] /Users/russell.dunphy/Code/Personal/foam/src/main/scala/com/rsslldnphy/foam/ExampleService.scala:31: class Post of type com.rsslldnphy.foam.routing.Post does not take type parameters. [error] case Post[Person] (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p)) - person Russell; 14.11.2012
comment
Извините, скачал код github и будет компилироваться. Пять минут - person Reuben Doetsch; 14.11.2012
comment
Нет, извините, я понял сложность и искал сообщение в блоге, которое я нашел по этой теме. вечером посмотрю еще - person Reuben Doetsch; 15.11.2012
comment
Спасибо, что нашли время. Я полностью застрял, и это меня раздражает! - person Russell; 15.11.2012