Scala преобразует объект модели в DTO

#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 действительно сложный..