Scala For Data Science 책에 소개된 json->객체, 객체 ->json 예제로서 내용이 좋아서 조금 수정해봤다.



Repo.scala


package models


case class Repo(

  val name: String,

  val language: String,

  val size: Long

)




Api.scala

package controllers


import models.Repo

import play.api.libs.json._

import play.api.mvc._


class Api extends Controller {

  implicit val writeRepos = new Writes[Repo] {

    override def writes(repo: Repo) = Json.obj(

      "name" -> repo.name,

      "language" -> repo.language,

      "size" -> repo.size

    )

  }


  val data = List[Repo](

    Repo("backend", "scala", 1),

    Repo("frontend", "jquery", 2)

  )


  def repos(username: String) = Action {

    val repoArray = Json.toJson(data)

    Ok(repoArray)

  }

}



conf/routes 파일에 다음을 추가한다.

GET     /api/repos/:username        controllers.Api.repos(username)



웹 브라우져에서 http://127.0.0.1:9000/api/repos/odersky을 호출한다. 



결과


[{"name":"backend","language":"scala","size":1},{"name":"frontend","language":"jquery","size":2}]




Play 프레임워크에서는 Writes[T] 타입 클래스는 단 하나의 메소드를 가진다.


trait Writes[T] {

  def writes(obj:T):Json

}


Writes 메소드는 Play 프레임워크에 내장되어 있어서 Writes를 직접 구현하지 않아도 된다.


Writes[Repo] 인스턴스는 Api controller 코드에서 정의해서 Api controller 내부에서 사용할 수 있도록 한다. 

Writes[Repo] 타입 클래스는 Repo 인스턴스를 JSON으로 변환하는 방법을 안다. JSON HTTP 응답을 생성하는 방법을 알고 있기 때문에 깔끔한 코드를 짤 수 있다.


자바라면 맵/배열을 이용하거나 custom Serializer 를 사용해야 한다(예, http://www.baeldung.com/jackson-map)


이를 편하게 만들어준다고 할 수 있는 큰 기능인 것 같다.




이제는 json을 객체로 변환하는 코드 테스트이다. 


controllers.Api 클래스의 코드를 다음과 같이 변경한다.




package controllers


import models.Repo

import play.api.mvc._

import play.api.libs.ws.WS

import play.api.Play.current

import play.api.libs.json._

import play.api.libs.functional.syntax._

import play.api.libs.concurrent.Execution.Implicits.defaultContext


class Api extends Controller {

  // Repo -> json

  implicit val writeRepos = new Writes[Repo] {

    override def writes(repo: Repo) = Json.obj(

      "name" -> repo.name,

      "language" -> repo.language,

      "size" -> repo.size

    )

  }


  // json -> Repo

  implicit val default:Reads[Repo] = (

    (JsPath \ "name").read[String] and

      (JsPath \ "language").read[String] and

      (JsPath \ "size").read[Long]

    )(Repo.apply _)


  val data = List[Repo](

    Repo("backend", "scala", 1),

    Repo("frontend", "jquery", 2)

  )


  def repos(username:String) = Action.async {


    val url = s"https://api.github.com/users/$username/repos"

    val response = WS.url(url).get()


    response.map { r =>

      if (r.status == 200) {

        val reposOpt = Json.parse(r.body).validate[List[Repo]]

        reposOpt match {

          case JsSuccess(repos, _) => Ok(Json.toJson(repos))

          case _ => InternalServerError

        }

      }

      else {

        NotFound

      }

    }

  }

}



https://api.github.com/users/odersky/repos의 코드를 실행해 json 파싱하고 Read[Repo]타입 클래스에 저장하도록 한다. 




Posted by '김용환'
,