Documentation

You are viewing the documentation for the 2.3.x release series. The latest stable release series is 2.4.x.

§JSON Reads/Writes/Format Combinator’lar

JSON temelleri sayfasında JsValue yapıları ile diğer veri türleri arasında dönüşüm yapmak için kulanılan Reads ve Writes dönüştürücüleri tanıtmıştık. Bu sayfada ise bu dönüştürücülerin nasıl oluşturulacağını ve dönüşüm esnasında nasıl doğrulama yapılacağını çok daha ayrıntılı anlatacağız.

Bu sayfadaki örnekler aşağıdaki JsValue yapısını ve ona karşılık gelen modeli kullanacak:

import play.api.libs.json._

val json: JsValue = Json.parse("""
{
  "name" : "Watership Down",
  "location" : {
    "lat" : 51.235685,
    "long" : -1.309197
  },
  "residents" : [ {
    "name" : "Fiver",
    "age" : 4,
    "role" : null
  }, {
    "name" : "Bigwig",
    "age" : 6,
    "role" : "Owsla"
  } ]
}
""")
case class Location(lat: Double, long: Double)
case class Resident(name: String, age: Int, role: Option[String])
case class Place(name: String, location: Location, residents: Seq[Resident])

§JsPath

JsPath, Reads/Writes oluşturmak için temel yapı taşıdır. JsPath verinin bir JsValue yapısı içindeki konumunu ifade eder. JsPath nesnesini (kök yol) kullanarak JsValue içinde gezinme sözdizimine benzer şekilde bir çocuk JsPath örneği tanımlayabilirsiniz:

import play.api.libs.json._

val json = { ... }

// Simple path
val latPath = JsPath \ "location" \ "lat"

// Recursive path
val namesPath = JsPath \\ "name"

// Indexed path
val firstResidentPath = (JsPath \ "residents")(0)

play.api.libs.json paketi JsPath için bir öteki ad tanımlar: __ (çift altçizgi). Eğer isterseniz bunu şu şekilde kullanabilirsiniz:

val longPath = __ \ "location" \ "long"

§Reads

Reads dönüştürücüler bir JsValue’dan başka bir türe dönüşüm yapmak için kullanılırlar. Daha karmaşık Reads oluşturmak için birden fazla Reads bir araya getirebilirsiniz.

Reads oluşturabilmek için aşağıdaki import’lara ihtiyacınız olacak:

import play.api.libs.json._ // JSON library
import play.api.libs.json.Reads._ // Custom validation helpers
import play.api.libs.functional.syntax._ // Combinator syntax

§Yol Reads

JsPath bir JsValue’ya belirtilen yolda başka bir Reads uygulayan özel bir Reads oluşturmak için metotlara sahiptir:

Not: JSON kütüphanesi String, Int, Double, vb. temel türler için örtük Reads sağlar.

Tek bir yol Reads tanımı şöyledir:

val nameReads: Reads[String] = (JsPath \ "name").read[String]

§Karmaşık Reads

Bağımsız yol Reads tanımlarını bir araya getirerek karmaşık modelleri dönüştürmek için daha karmaşık Reads oluşturabilirsiniz.

Daha anlaşılır olması için birleştirme özelliğini iki parçaya ayıracağız. Önce Reads nesnelerini and combinator kullanarak birleştirin:

val locationReadsBuilder = 
  (JsPath \ "lat").read[Double] and 
  (JsPath \ "long").read[Double]

Bu FunctionalBuilder[Reads]#CanBuild2[Double, Double] şeklinde bir tür oluşturacaktır. Bu ara nesne hakkında çok fazla düşünmenize gerek yoktur. Karmaşık Reads oluşturmak için kullanıldığını bilmeniz yeterli.

Daha sonra CanBuildX‘in apply metodunu değerleri modelinize dönüştürecek bir fonksiyon ile çağırın. Bu bir karmaşık Reads döndürecektir. Eğer bir case class’ınız varsa doğrudan onun apply metodunu kullanabilirsiniz.

implicit val locationReads = locationReadsBuilder.apply(Location.apply _)

Aynı kod aşağıda tek bir ifade olarak verilmiştir:

implicit val locationReads: Reads[Location] = (
  (JsPath \ "lat").read[Double] and
  (JsPath \ "long").read[Double]
)(Location.apply _)

§Reads ile doğrulama

JSON temelleri sayfasında JsValue.validate metodunun bir JsValue’dan başka bir türe dönüşüm ve doğrulama için tercih edilen yol olduğundan bahsetmiştik. Temel desen aşağıda yer alıyor:

val json = { ... }

val nameReads: Reads[String] = (JsPath \ "name").read[String]

val nameResult: JsResult[String] = json.validate[String](nameReads)

nameResult match {
  case s: JsSuccess[String] => println("Name: " + s.get)
  case e: JsError => println("Errors: " + JsError.toFlatJson(e).toString()) 
}

Reads için varsayılan doğrulama tür dönüşüm hataları gibi çok basit doğrulamalardan ibarettir. Fakat Reads doğrulama yardımcılarını kullanarak özel doğrulama kuralları tanımlayabilirsiniz. Çok kullanılanlardan bazıları şöyle:

Doğrulama eklemek için yardımcı metotları JsPath.read metoduna argümanlar olarak uygulayın:

val improvedNameReads = 
  (JsPath \ "name").read[String](minLength[String](2))

§Hepsini bir araya getirelim

Karmaşık Reads ve özel doğrulama kullanarak örnek modelimiz için etkin bir Reads seti tanımlayabilir ve uygulayabiliriz:

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

implicit val locationReads: Reads[Location] = (
  (JsPath \ "lat").read[Double](min(-90.0) keepAnd max(90.0)) and
  (JsPath \ "long").read[Double](min(-180.0) keepAnd max(180.0))
)(Location.apply _)

implicit val residentReads: Reads[Resident] = (
  (JsPath \ "name").read[String](minLength[String](2)) and
  (JsPath \ "age").read[Int](min(0) keepAnd max(150)) and
  (JsPath \ "role").readNullable[String]
)(Resident.apply _)

implicit val placeReads: Reads[Place] = (
  (JsPath \ "name").read[String](minLength[String](2)) and
  (JsPath \ "location").read[Location] and
  (JsPath \ "residents").read[Seq[Resident]]
)(Place.apply _)


val json = { ... }

json.validate[Place] match {
  case s: JsSuccess[Place] => {
    val place: Place = s.get
    // do something with place
  }
  case e: JsError => {
    // error handling flow
  }
}

Karmaşık Reads nesneleri iç içe geçebilir. Bu örnekte placeReads belirli yollarda daha önceden tanımlanmış örtük locationReads ve residentReads tanımlarını kullanır.

§Writes

Writes dönüştürücüler bir türden JsValue’ya dönüşüm yapmak için kullanılırlar.

Reads’e benzer şekilde JsPath ve combinator’lar kullanarak karmaşık Writes oluşturabilirsiniz. Örneğimiz için Writes aşağıdaki gibidir:

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

implicit val locationWrites: Writes[Location] = (
  (JsPath \ "lat").write[Double] and
  (JsPath \ "long").write[Double]
)(unlift(Location.unapply))

implicit val residentWrites: Writes[Resident] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "age").write[Int] and
  (JsPath \ "role").writeNullable[String]
)(unlift(Resident.unapply))

implicit val placeWrites: Writes[Place] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "location").write[Location] and
  (JsPath \ "residents").write[Seq[Resident]]
)(unlift(Place.unapply))


val place = Place(
  "Watership Down",
  Location(51.235685, -1.309197),
  Seq(
    Resident("Fiver", 4, None),
    Resident("Bigwig", 6, Some("Owsla"))
  )
)

val json = Json.toJson(place)

Writes ve Reads arasında bazı farklılıklar vardır:

§Özyinelemeli Türler

Örneğimizin ele almadığı özel bir durum özyinelemeli türler için Reads ve Writes kullanımının nasıl olacağıdır. JsPath call-by-name parametreler alan lazyRead ve lazyWrite metodları sunar:

case class User(name: String, friends: Seq[User])

implicit lazy val userReads: Reads[User] = (
  (__ \ "name").read[String] and
  (__ \ "friends").lazyRead(Reads.seq[User](userReads))
)(User)

implicit lazy val userWrites: Writes[User] = (
  (__ \ "name").write[String] and
  (__ \ "friends").lazyWrite(Writes.seq[User](userWrites))
)(unlift(User.unapply))

§Format

Format[T] yalnızca Reads ve Writes trait’lerinin karışımıdır ve bileşenlerinin yerine örtük dönüştürme için kullanılabilir.

§Reads ve Writes ile Format yaratmak

Aynı türün Reads ve Writes’larını kullanarak bir Format tanımlayabilirsiniz:

val locationReads: Reads[Location] = (
  (JsPath \ "lat").read[Double](min(-90.0) keepAnd max(90.0)) and
  (JsPath \ "long").read[Double](min(-180.0) keepAnd max(180.0))
)(Location.apply _)

val locationWrites: Writes[Location] = (
  (JsPath \ "lat").write[Double] and
  (JsPath \ "long").write[Double]
)(unlift(Location.unapply))

implicit val locationFormat: Format[Location] =
  Format(locationReads, locationWrites)

§Combinator’lar kullanarak Format yaratmak

Eğer Reads ve Writes’ınız simetrik ise (gerçek uygulamalarda sık rastlanmayabilir) combinator’lar aracılığıyla doğrudan bir Format tanımlayabilirsiniz:

implicit val locationFormat: Format[Location] = (
  (JsPath \ "lat").format[Double](min(-90.0) keepAnd max(90.0)) and
  (JsPath \ "long").format[Double](min(-180.0) keepAnd max(180.0))
)(Location.apply, unlift(Location.unapply))

Sonraki: JSON Transformer'lar


Dokümantasyonun bu çevirisi Play ekibi tarafından yapılmamaktadır. Eğer bir hata bulduysanız, bu sayfanın kaynak kodu burada bulunmaktadır. Dokümantasyon yönergelerini okuduktan sonra lütfen katkı yapmaktan çekinmeyin.