2016-04-08 3 views
1

Использование генераторов Scala Я пытаюсь отделить некоторые общие функции в приложении Play. Функции возвращают Seq с объектами, десериализованными из службы REST JSON.Тип возврата функции Scala на основе общих

def getPeople(cityName: String): Future[Seq[People]] = { 
    getByEndpoint[People](s"http://localhost/person/$cityName") 
} 

def getPeople(): Future[Seq[Dog]] = { 
    getByEndpoint[Dog]("http://localhost/doge") 
} 

Логика выборки и десериализации упакована в единую функцию с использованием дженериков.

private def getByEndpoint[T](endpoint: String): Future[Seq[T]] = { 

    ws.url(endpoint) 
     .get() 
     .map(rsp => rsp.json) 
     .flatMap { json => 
     json.validate[Seq[T]] match { 
      case s: JsSuccess[Seq[T]] => 
      Future.successful(s.get) 
      case e: JsError => 
      Future.failed(new RuntimeException(s"Get by endpoint JSON match failed: $e")) 
     } 
     } 

} 

Проблема в том, что я получаю «No Json deserializer found for type Seq[T]. Try to implement an implicit Reads or Format for this type.». Я уверен, что не использую T в Seq[T] (по крайней мере, по моим моделям C#/Java), но я не могу найти подсказки, как это сделать в Scala. Все работает так, как ожидалось, без использования дженериков.

+1

Попробуйте изменить 'def getByEndpoint [T]' на 'def getByEndpoint [T: Format]'? Если это сработает для вас, я с удовольствием напишу ответ, объясняющий, почему. –

+0

Он делает! Я предполагаю, что это имеет какое-то отношение к 'Json.format'у, который я установил на моделях. Можете ли вы отправить ответ, чтобы я мог отметить его как ответ? – giannoug

ответ

2

Воспроизведение JSON использует type classes для сбора информации о том, какие типы могут быть (де-) сериализованы в JSON и из них. Если у вас есть неявное значение типа Format[Foo] в области видимости, это называется экземпляром класса Format для Foo.

Преимущество этого подхода заключается в том, что оно дает нам возможность ограничить общие типы (и эти ограничения проверяются во время компиляции), которые не зависят от подтипирования. Например, нет никакой возможности, что String стандартной библиотеки когда-либо расширит какую-либо характеристику Jsonable, которую может предоставить Play (или любая другая библиотека), поэтому нам нужно как-то сказать «мы знаем, как кодировать String s как JSON», t вовлекать String подтип некоторой черты, которую мы сами определили.

В Play JSON вы можете сделать это путем определения неявных Format экземпляров, а сама игра обеспечивает многие из них для вас (например, если у вас есть один для T, это даст вам один для Seq[T]). Метод validate на JsValue требует одного из этих экземпляров (на самом деле подтип Format, Reads, но здесь это не очень важно) для его параметра типа - Seq[T] в этом случае - и он не будет компилироваться, если компилятор не сможет найти этот экземпляр.

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

private def getByEndpoint[T: Format](endpoint: String): Future[Seq[T]] = { 
    ... 
} 

Теперь с синтаксисом T: Format вы указали, что там должен быть Format экземпляр для T (даже если вы не 't ограничение T любым другим способом), поэтому компилятор знает, как предоставить экземпляр Format для Seq[T], который требует вызов json.validate[Seq[T]].

Смежные вопросы