JSON OBJECTS RDBMSTO TO
Stephen Kemmerling
@42eng
CONTACTS AS A SERVICE
Should have JSON endpoints for
• Store contact information
• Retrieval by Name
Contacts as a Service...
• OO Representation
• Handle JSON requests and convert to OO
• Save to DB/Load from DB
What do we need?
• OO Representation
• Handle JSON requests and convert to OO
• Save to DB/Load from DB
What do we need?
SCALA REPRESENTATION
case class Email(addr: String)
case class Contact(
name: String,
phone: Option[Int],
email: Option[Em...
• OO Representation
• Handle JSON requests and convert to OO
• Save to DB/Load from DB
What do we need?
SENDING JSON RESPONSES
def lookup(name: String) = Action { request =>
val contacts : Seq[Contact] = loadFromDb(name)
val c...
TO/FROM JSON
trait Format[T] {
def reads(json: JsValue): JsResult[T]
def writes(obj: T): JsValue
}
MACROS!
object Contact {
implicit val format: Format[Contact] =
Json.format[Contact]
}
Almost, but not quite: Can’t deal w...
FORMAT BY HAND
object Email {
implicit format : Format[Email] = Json.format[Email]
}
Let’s not be lazy
FORMAT BY HAND
object Email {
implicit val format = new Format[Email]{
def reads(json: JsValue) : JsResult[Email] = {
json...
ACCEPTING JSON
REQUESTS
def store(name: String) = Action(parse.tolerantJson)
{ request =>
val phoneJson : JsValue = reques...
ALL TOGETHER NOW
def lookup(name: String) = Action { request =>
val contacts : Seq[Contact] = loadFromDb(name)
val contact...
• OO Representation
• Handle JSON requests and convert to OO
• Save to DB/Load from DB
What do we need?
SLICK TABLE DEFINITION
object Contacts extends Table[Contact]("contacts") {
def name = column[String]("name", O.PrimaryKey...
INSERTS AND LOOKUPS
def saveToDb(contact: Contact) =
database.withSession{ implicit session: Session =>
Contacts.*.insert(...
SLICK TABLE DEFINITION
object Contacts extends Table[Contact]("contacts") {
def name = column[String]("name", O.PrimaryKey...
TYPE MAPPER
implicit object typeMapper extends BaseTypeMapper[Email] {
def apply(profile: BasicProfile) : TypeMapperDelega...
e class Email(addr: String)
ect Email {mplicit val format = new
mat[Email]{def reads(json: JsValue) :
sult[Email] = {
json...
Upcoming SlideShare
Loading in …5
×

Json and SQL DB serialization Introduction with Play! and Slick

8,360 views

Published on

Published in: Technology

Json and SQL DB serialization Introduction with Play! and Slick

  1. 1. JSON OBJECTS RDBMSTO TO Stephen Kemmerling @42eng
  2. 2. CONTACTS AS A SERVICE Should have JSON endpoints for • Store contact information • Retrieval by Name Contacts as a Service https://github.com/FortyTwoEng/ Contacts-As-A-Service/blob/master/ app/controllers/Application.scala
  3. 3. • OO Representation • Handle JSON requests and convert to OO • Save to DB/Load from DB What do we need?
  4. 4. • OO Representation • Handle JSON requests and convert to OO • Save to DB/Load from DB What do we need?
  5. 5. SCALA REPRESENTATION case class Email(addr: String) case class Contact( name: String, phone: Option[Int], email: Option[Email] )
  6. 6. • OO Representation • Handle JSON requests and convert to OO • Save to DB/Load from DB What do we need?
  7. 7. SENDING JSON RESPONSES def lookup(name: String) = Action { request => val contacts : Seq[Contact] = loadFromDb(name) val contactsJson : Seq[JsValue] = contacts.map(Json.toJson(_)) Ok(JsArray(contactsJson)) }
  8. 8. TO/FROM JSON trait Format[T] { def reads(json: JsValue): JsResult[T] def writes(obj: T): JsValue }
  9. 9. MACROS! object Contact { implicit val format: Format[Contact] = Json.format[Contact] } Almost, but not quite: Can’t deal with Email case class Email(addr: String) case class Contact( name: String, phone: Option[Int], email: Option[Email] ) MAGIC!
  10. 10. FORMAT BY HAND object Email { implicit format : Format[Email] = Json.format[Email] } Let’s not be lazy
  11. 11. FORMAT BY HAND object Email { implicit val format = new Format[Email]{ def reads(json: JsValue) : JsResult[Email] = { json match{ case JsString(s) => JsSuccess(Email(s)) case _ => JsError() } } def writes(email: Email) : JsValue = { JsString(email.addr) } } }
  12. 12. ACCEPTING JSON REQUESTS def store(name: String) = Action(parse.tolerantJson) { request => val phoneJson : JsValue = request.body "phone" val phone : Option[Int] = phoneJson.asOpt[Int] val email = (request.body "email").asOpt[Email] val contact = Contact(name, phone, email) saveToDb(contact) Ok(“”) }
  13. 13. ALL TOGETHER NOW def lookup(name: String) = Action { request => val contacts : Seq[Contact] = loadFromDb(name) val contactsJson : Seq[JsValue] = contacts.map(Json.toJson(_)) Ok(JsArray(contactsJson)) } def store(name: String) = Action(parse.tolerantJson) { request => val phoneJson : JsValue = request.body "phone" val phone : Option[Int] = phoneJson.asOpt[Int] val email = (request.body "email").asOpt[Email] val contact = Contact(name, phone, email) saveToDb(contact) Ok(“”) }
  14. 14. • OO Representation • Handle JSON requests and convert to OO • Save to DB/Load from DB What do we need?
  15. 15. SLICK TABLE DEFINITION object Contacts extends Table[Contact]("contacts") { def name = column[String]("name", O.PrimaryKey) def phone = column[Int]("phone", O.Nullable) def email = column[Email]("email", O.Nullable) def * = name ~ phone.? ~ email.? <> (Contact.apply _, Contact.unapply _) }
  16. 16. INSERTS AND LOOKUPS def saveToDb(contact: Contact) = database.withSession{ implicit session: Session => Contacts.*.insert(contact) } def loadFromDb(name: String) = database.withSession{ implicit session: Session => (for (row <- Contacts if row.name===name) yield row).list }
  17. 17. SLICK TABLE DEFINITION object Contacts extends Table[Contact]("contacts") { def name = column[String]("name", O.PrimaryKey) def phone = column[Int]("phone", O.Nullable) def email = column[Email]("email", O.Nullable) def * = name ~ phone.? ~ email.? <> (Contact.apply _, Contact.unapply _) } def saveToDb(contact: Contact) = database.withSession{ implicit session: Session => Contacts.*.insert(contact) } def loadFromDb(name: String) = database.withSession{ implicit session: Session => (for (row <- Contacts if row.name===name) yield row).list }
  18. 18. TYPE MAPPER implicit object typeMapper extends BaseTypeMapper[Email] { def apply(profile: BasicProfile) : TypeMapperDelegate[Email] = { val delegate = profile.typeMapperDelegates.stringTypeMapperDelegate new TypeMapperDelegate[Email] { def sqlType = delegate.sqlType def setValue(value: Email, p: PositionedParameters) = delegate.setValue(value.addr, p) def setOption(valueOpt: Option[Email], p: PositionedParameters) = delegate.setOption(valueOpt.map(_.addr), p) def nextValue(r: PositionedResult): Email = Email(delegate.nextValue(r)) def sqlTypeName = delegate.sqlTypeName def updateValue(value: Email, r: PositionedResult) = delegate.updateValue(value.addr, r) def zero = Email("towel@42go.com") } } }
  19. 19. e class Email(addr: String) ect Email {mplicit val format = new mat[Email]{def reads(json: JsValue) : sult[Email] = { json match{case JsString(s) => cess(Email(s))case _ => JsError() } f writes(email: Email) : e = {sString(email.addr) p(name: String) = rse.json) { request => acts : Seq[Contact] = (name)actsJson : Seq[J ts.map(J object Contacts extends Table[Contact]("contacts") { def name = column[String] ("name", O.PrimaryKey) def phone = column[Int] ("phone", O.Nullable) def email = column[Email] ("email", O.Nullable) def * = name ~ phone.? ~ email.? <> (Contact.apply _, Contact.unapply _) } def saveToDb(contact: database.with session meters) = [Email], p: gate.setOption(valueOpt.map(_.addr), Value(r: PositionedResult): Email = delegate.nextValue(r)) def sqlTypeName = delegate.sqlTypeName def updateValue(value: Email, r: PositionedResult) = delegate.updateValue(value.addr, r) def zero = Email("towel@42go.com") } } } case class Contact( name: String, phone: Option[Int], email: Option[Email] ) object Contact { implicit format : Format[Contact] = } SOURCE: https://github.com/FortyTwoEng/Contacts-As-A-Service FOLLOW US: @42ENG JOIN US: 42GO.COM/JOIN_US.HTML

×