#scala #playframework #functional-programming #implicit-conversion
#scala #playframework #функциональное программирование #неявное преобразование
Вопрос:
Я новичок в scala и пытаюсь понять его базовые концепции. С использованием play / scala / slick я пытаюсь реализовать тривиальное приложение, которое позволяет пользователю выполнять операции CRUD с другими пользователями. Итак, проблема, с которой я сталкиваюсь, заключается в том, что я не понимаю, как я должен использовать неявные преобразования для преобразования объекта модели в объект DTO.
Вот что у меня пока есть:
Application.scala
(Класс контроллера):
//returns list of users
def users = Action.async {
val userList = userDAO.all()
userList
.map { list => Ok(list.map(elem => Json.toJson(elem))) }
.recover { case _ => InternalServerError }
}
User.scala
(объект модели, представляет запись в базе данных):
case class User(id: Long, login: String, password: String) extends BaseEntity
UserDto.scala
(DTO, объект, представляющий пользователя в списке пользователей):
case class UserDto(id: Long, login: String) {
implicit def userWriter = Json.writes[UserDto]
implicit def user2UserDto(user: User): UserDto = UserDto(user.id, user.login)
}
В приведенном выше коде, в теге # 1, я получаю сообщение об ошибке, в котором указано, что User
оно не может быть преобразовано в json. Точное сообщение об ошибке: Ошибка: (40, 53) Не найден сериализатор Json для типа Application.this.UserDao.Сущность. Попробуйте реализовать неявную запись или формат для этого типа. .map { список => Ok(list.map(элемент => Json.toJSON(элемент))) } . Как и где я должен реализовать преобразование из User в UserDTO, чтобы оно работало, а не, скажем, уродливо?
Например, в Java я бы реализовал каждый объект Dto с public User to()
помощью and public static UserDto from(User user)
, чтобы я мог их конвертировать. Должен ли я сделать то же самое в scala или есть более элегантный способ выполнить эту задачу?
РЕДАКЦИЯ Отредактированная версия users
метода:
def users = Action.async {
val userList = userDAO.all()
userList
.map { list => Ok(list.map(elem => Json.toJson(elem : UserDto))) }
.recover { case _ => InternalServerError }
}
Есть следующие ошибки компилятора:
Error:(41, 24) not enough arguments for method apply: (implicit writeable: play.api.http.Writeable[Seq[play.api.libs.json.JsValue]])play.api.mvc.Result in class Status.
Unspecified value parameter writeable.
.map { list => Ok(list.map(elem => Json.toJson(elem : UserDto))) }
Error:(41, 24) Cannot write an instance of Seq[play.api.libs.json.JsValue] to HTTP response. Try to define a Writeable[Seq[play.api.libs.json.JsValue]]
.map { list => Ok(list.map(elem => Json.toJson(elem : UserDto))) }
Кажется, что компилятор не видит userWriter
в UserDto
объекте.
Комментарии:
1.
userList .map { list => Ok(userList.map(list => list.map(elem => Json.toJson(elem)))) }
Почему вы сопоставляетеuserList
, но не используете ни один из элементов в нем, а вместо этого сопоставляете его заново для каждого элемента? Такжеlist.map(elem => Json.toJson(elem))
может быть записан какlist.map(Json.toJson)
. Пожалуйста, укажите точное сообщение об ошибке, которое вы получаете при вызовеtoJson
.2. @sirius, я добавил сообщение об ошибке, которое я получаю при компиляции. Пожалуйста, смотрите Обновленный ответ.
Ответ №1:
Не определяйте импликации внутри класса case. Определите их в сопутствующем объекте:
case class UserDto(id: Long, login: String)
object UserDto {
implicit def userWriter = Json.writes[UserDto]
implicit def user2UserDto(user: User): UserDto = UserDto(user.id, user.login)
}
Когда вы определяете их внутри класса case, они становятся методами экземпляра, доступными только из экземпляра класса. Когда вы определяете их в сопутствующем объекте, они определяются глобально (не привязаны к экземпляру) и эквивалентны статическим методам в java. Оттуда механизм неявного разрешения scala может их найти.
Комментарии:
1. Спасибо, Альваро! Я исправил эту ошибку, но, к сожалению, ошибка компиляции не исчезла.
2. Чтобы заставить неявное работать, ему нужен этот путь:
User -> UserDTO -> Writes[UserDTO]
. Но импликации не цепляются так автоматически. Вам нужно явно преобразоватьUser
UserDTO
в или немного помочь компилятору, указав описание типа:Json.toJson(elem : UserDTO)
3. Большое спасибо, Альваро. Я смог решить эту проблему, но затем компилятор начал жаловаться
writes
… Я обновляю код, и вот ошибка:Error:(41, 24) Cannot write an instance of Seq[play.api.libs.json.JsValue] to HTTP response. Try to define a Writeable[Seq[play.api.libs.json.JsValue]] .map { list => Ok(list.map(elem => Json.toJson(elem : UserDto))) }
. Переход на scala действительно сложный..