Playframework(9)Scala Project and Working with JSON

Playframework(9)ScalaProjectandWorkingwithJSON

5.WorkingwithJSON

5.1ThePlayJSONlibrary

WerecommendtodealwithJSONbasedthetypeclasslibraryplay.api.libs.json.

Thislibraryisbuiltontopofhttps://github.com/codahale/jerkson/,whichisaScalawrapperaroundthesuper-fastjavabasedJSONlibrary,Jackson.

TherearesomeJSONdatatypes:

JsObject

JsNull

JsUndefined

JsBoolean

JsNumber

JsArray

JsString

AlloftheminheritfromthegenericJSONvalue,JsValue.

ParsingaJsonString

valjson:JsValue=Json.parse(jsonString)

NavigatingintoaJsontree

AssoonaswehaveaJsValuewecannavigateintothetree.

valjson=Json.parse(jsonString)

valmaybeName=(json\"user"\name).asOpt[String]

valemails=(json\"user"\\"emails").map(_.as[String])

asOpt[T]thatwillreturnNoneifthevalueismissing.

Otherwisewecanuseas[T]thatwefailwithanexceptionifthevaluewasmissing.

ConvertingaScalavaluetoJson

valjsonNumber=Json.toJson(4)

valjsonArray=Json.toJson(Seq(1,2,3,4))

ItiscomplicatediftheSeqcontainssomeothervaluesthanInt,forexample:

Seq(1,"Carl",3,4)

valjsonArray=Json.toJson(Seq(

toJson(1),toJson("Carl"),toJson(3),toJson(4)

))

SerializingJson

valjsonString:String=Json.stringify(jsValue)

OtherOptions

…snip...

5.2HandlingandservingJSONrequests

defsayHi=Action{reqeust=>

request.body.asJson.map{json=>

(json\"name").asOpt[String].map{name=>

Ok("Hello"+name)

}.getOrElse{

BadRequest("Missingparameter[name]")

}

}.getOrElse{

BadRequest("ExpectingJsondata")

}

}

BetterandSimplertospecifyourownBodyParser

defsayHi=Action(parse.json){request=>

(request.body\"name").asOpt[String].map{name=>

Ok("Hello"+name)

}.getOrElse{

…snip...

}

}

WecanusecommandlinecURL

>curl

--header"Content-type:application/json"

--requestPOST

--data'{"name":"Carl"}'

http://localhost:9000/sayHi

ServingaJSONresponse

defsayHi=Action(parse.json){request=>

(request.body\"name").asOpt[String].map{name=>

Ok(toJson(

Map("status"->"OK","message"->("Hello"+name))

))

}.getOrElse{

BadRequest(toJson(

Map("status"->"KO","message"->"Missingparameter[name]")

))

}

}

6.WorkingwithXML

HandlinganXMLrequest

defsayHi=Action{request=>

request.body.asXml.map{xml=>

(xml\\"name"headOption).map(_.text).map{name=>

Ok("Hello"+name)

}.getOrElse{

BadRequest("Missingparameter[name]")

}

}.getOrElse{

BadRequest("ExpectingXMLdata")

}

}

defsayHi=Action(parse.xml){request=>

(request.body\\"name"headOption).map(_.text).map{name=>

Ok("Hello"+name)

}.getOrElse{

BadRequet("Missingparameter[name])

}

}

ServinganXMLresponse

defsayHi=Action(parse.xml){request=>

(request.body\\"name"headOption).map(_.text).map{name=>

Ok(<messagestatus="OK">Hello{name}</message>)

}.getOrElse{

BadRequest(<messagestatus="KO">Missingparameter[name]</message>)

}

}

7Handlingfileupload

Uploadingfilesinaformusingmultipart/form-data

@form(action=routes.Application.upload,'enctype->"multipart/form-data")

<inputtype="file"name="picture">

<p><inputtype="submit"></p>

}

defupload=Action(parse.multipartFormData){request=>

request.body.file("picture").map{picture=>

importjava.io.File

valfilename=picture.filename

valcontentType=picture.contentType

picture.ref.moveTo(newFile("/tmp/picture"))

Ok("Fileuploaded")

}.getOrElse{

Redirect(routes.Application.index).flashing(

"error"->"Missingfile"

)

}

}

TherefattributegivesmeareferencetoaTemporaryFile.

Directfileupload

AJAXuploadthefileasynchronouslyinaform.

WritingOurOwnBodyParser

8.AccessinganSQLdatabase

8.1ConfiguringandUsingJDBC

SQLitedatabaseengineconnectionproperties

db.default.driver=org.sqlite.JDBC

db.default.url="jdbc:sqlite:/path/to/db-file"

AccessingtheJDBCdatasource

Theplay.api.dbpackageprovidesaccesstotheconfigureddatasources:

valds=DB.getDataSource()

ObtainingaJDBCconnection

valconnection=DB.getConnection()

Weneedtocallcloseafterweusetheconnection.Wecandothatinanotherway:

//access"default"database

DB.withConnection{conn=>

//dowhateveryouneedwiththeconnection

}

//access"orders"databaseinsteadof"default"

DB.withConnection("orders"){conn=>

//

}

8.2Anorm,simpleSQLdataaccess

PlayincludesasimpledataaccesslayercalledAnormthatusesplainSQLtointeractwiththedatabaseandprovidesanAPItoparseandtransformtheresultingdatasets.

mysqlconfiguration

db.default.driver=com.mysql.jdbc.Driver

db.default.url="jdbc:mysql://localhost/world"

db.default.user=root

db.default.password=111111

Overview

ItcanfeelstrangetoreturntoplainoldSQLtoaccessanSQLdatabasethesedays.JavaDevelopersaccustomedtousingahigh-levelObjectRelationalMapperlikeHibernate.

Butwethinkthesetoolsarenotneededatallwhenyouhavethepowerofahigh-levelprogramminglanguagelikeScala.Onthecontrary,theywillquicklybecomecounter-productive.

UsingJDBCisapain,butweprovideabetterAPI

Youdon'tneedanotherDSLtoaccessrelationaldatabases

SQLisalreadythebestDSLforaccessingrelationaldatabases.Wedon'tneedtoinventsomethingnew.

AtypesafeDSLtogenerateSQLisamistake

TakeControlofyourSQLcode

ExecutingSQLqueries

importanorm._

DB.withConnection{implicitc=>

valresult:Boolean=SQL("Select1").execute()

}

Theexecute()methodreturnsaBooleanvalueindicatingwhethertheexecutionwassuccessful.

Toexecuteanupdate,youcanuseexecuteUpdate(),whichreturnsthenumberofrowsupdated.

valresult:Int=SQL("deletefromCitywhereid=99").executeUpdate()

Ifweplantoinsertdatathathasanauto-generatedLongprimarykey,youcancallexecuteInsert().

valid:Int=SQL("insertintoCity(name,country)values({name},{country}")

.on("Cambridge","NewZealand").executeInsert();

SinceScalasupportsmulti-linestrings,feelfreetousethemforcomplexSQLstatement:

valsqlQuery=SQL(

"""

select*fromCountryc

joinCountryLanguagelonl.CountryCode=c.Code

wherec.code='FRA';

"""

)

IfourSQLqueryneedsdynamicparameters,wecandeclareplaceholderslike{name}inthequerystring,andlaterassignavaluetothem:

SQL(

"""

select*fromCountryc

joinCountryLanguagelonl.CountryCode=c.Code

wherec.code={countryCode};

"""

).on("countryCode"->"FRA")

RetrievingdatausingtheStreamAPI

ThefirstwaytoaccesstheresultsofaselectqueryistousetheStreamAPI.

Whenyoucallapply()onanySQLstatement,youwillreceivealazyStreamofRowinstances,whereeachrowcanbeseenasadictionary:

//CreateanSQLquery

valselectCountries=SQL("Select*fromCountry")

//TransformtheresultingStream[Row]toaList[(String,String)]

valcountries=selectCountries().map(row=>

row[String]("code")->row[String]("name")

).toList

CountthenumberofCountryentriesinthedatabase,sotheresultsetwillbeasinglerowwithasinglecolumn:

valfirstRow=SQL("Selectcount(*)ascfromCountry").apply().head

//Nextgetthecontentofthe'c'columnasLong

valcountryCount=firstRow[Long]("c")

UsingPatternMatching

…snip…

Specialdatatypes

Clobs

SQL("Selectname,summaryfromCountry")().map{

caseRow(name:String,summary:java.sql.Clob)=>name->summary

}

Binary

SQL("Selectname,imagefromCountry")().map{

caseRow(name:String,image:Array[Byte])=>name->image

}

DealingwithNullablecolumns

IfacolumncancontainNullvaluesinthedatabaseschema,youneedtomanipulateitasanOptiontype.

SQL("Selectname,indepYearfromCountry")().collect{

caseRow(name:String,Some(year:int))=>name->year

}

IfwetrytoretrievethecolumncontentasIntdirectlyfromthedictionary

SQL("Selectname,indepYearfromCountry")().map{row=>

row[String]("name")->row[Int]("indepYear")

}

ThiswillproduceanUnexpectedNullableFound(COUNTRY.INDEPYEAR)exceptionifitencountersanullvalue.

WeneedtowritelikethistoanOption[Int]

SQL("Selectname,indepYearfromCountry")().map{row=>

row[String]("name")->row[Option[Int]]("indepYear")

}

References:

http://www.playframework.org/documentation/2.0.4/ScalaHome

http://www.playframework.org/documentation/2.0.4/ScalaXmlRequests

相关推荐