Play JSON is a powerful Scala JSON library, originally developed by the Play team for use with Play Framework. It uses Jackson for JSON parsing and has no Play dependencies.
We've provided some documentation here on how to use Play JSON in your app (without Play). For more information on how to use Play JSON in Play, please refer to the Play documentation.
To get started, you can add play-json as a dependency in your project:
- sbt
libraryDependencies +="org.playframework"%%"play-json"%-version-
- Gradle
compile group: 'org.playframework', name: 'play-json_2.13', version: -version- - Maven
<dependency> <groupId>org.playframework</groupId> <artifactId>play-json_2.13</artifactId> <version>-version-</version> </dependency>
See GitHub releases for the correct version.
Play JSON supports Scala 2.12, 2.13 and Scala 3.3+. Choosing the right JAR is automatically managed in sbt. If you're using Gradle or Maven then you need to use the correct version in the artifactId.
The base type in Play JSON is play.api.libs.json.JsValue, and has several subtypes representing different JSON types:
JsObject: a JSON object, represented as a Map. Can be constructed from an orderedSeqor any kind ofMapusingJsObject.applyJsArray: a JSON array, consisting of aSeq[JsValue]JsNumber: a JSON number, represented as aBigDecimal.JsString: a JSON string.JsBoolean: a JSON boolean, eitherJsTrueorJsFalse.JsNull: the JSONnullvalue.
The play.api.libs.json package includes several features for constructing JSON from scratch, as well as for converting to and from case classes.
The play.api.libs.json.Json object has several methods for reading and writing:
Json.parse parses a JSON string or InputStream into a JSON tree:
valjson: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" } ]}""")and Json.stringify is used to convert a JsValue to a String of JSON:
valjsonString=Json.stringify(json)
// {"name":"Watership Down","location":{"lat":51.235685,"long":-1.309197},"residents":[{"name":"Fiver","age":4,"role":null},{"name":"Bigwig","age":6,"role":"Owsla"}]}Play JSON provides a traversal DSL that lets you query fields in the JSON:
Applying the \ operator will return the property corresponding to the field argument, supposing this is a JsObject.
vallat= (json \"location"\"lat").get
// returns JsNumber(51.235685)The (json \ "location" \ "lat") returns a JsLookupResult which may or may not contain a value. Note that the get operation is not always safe; it throws an exception if the path doesn't exist.
You can also use \ to look up indices within a JsArray:
valbigwig= (json \"residents"\1).get
// returns {"name":"Bigwig","age":6,"role":"Owsla"}Applying the \\ operator will do a lookup for the field in the current object and all descendants.
valnames= json \\"name"// returns Seq(JsString("Watership Down"), JsString("Fiver"), JsString("Bigwig"))You can retrieve a value in a JsObject or JsArray using an apply operator with the index number or key.
valname= json("name")
// returns JsString("Watership Down")valbigwig= json("residents")(1)
// returns {"name":"Bigwig","age":6,"role":"Owsla"}Like get, this will throw an exception if the index doesn't exist. Use the Simple Path \ operator and validate or asOpt (described below) if you expect that they key may not be present.
To convert a Scala object to and from JSON, we use Json.toJson[T: Writes] and Json.fromJson[T: Reads] respectively. Play JSON provides the Reads and Writes typeclasses to define how to read or write specific types. You can get these either by using Play's automatic JSON macros, or by manually defining them.
You can also read JSON from a JsValue using validate, as and asOpt methods. Generally it's preferable to use validate since it returns a JsResult which may contain an error if the JSON is malformed.
For example:
valunsafeName= (json \"name").as[String]
// "Watership Down"valunsafeBogusName= (json \"bogus").as[String]
// throws exceptionvalnameOption= (json \"name").asOpt[String]
// Some("Watership Down")valbogusOption= (json \"bogus").asOpt[String]
// NonevalnameResult= (json \"name").validate[String]
// JsSuccess("Watership Down")valbogusResult= (json \"bogus").validate[String]
// JsErrorvalunsafeName2= json("name").as[String]
// "Watership Down"valunsafeBogusName2= json("bogus").as[String]
// throws exceptionUsually you don't need to traverse JSON AST directly. Play JSON comes equipped with some convenient macros to convert to and from case classes.
For example, suppose I have the following class:
caseclassResident(name: String, age: Int, role: Option[String])I can define a Reads (JSON parser), Writes (JSON writer) using convenient macros:
implicitvalresidentReads=Json.reads[Resident]
implicitvalresidentWrites=Json.writes[Resident]I can also define a Format that does both:
implicitvalresidentFormat=Json.format[Resident]With the Reads and/or Writes in scope, I can then easily convert my class using toJson and fromJson
Play JSON provides a convenient functional DSL for constructing Reads and Writes. For example, assume I have the following classes:
caseclassLocation(lat: Double, long: Double)
caseclassResident(name: String, age: Int, role: Option[String])
caseclassPlace(name: String, location: Location, residents: Seq[Resident])Then I could construct Reads for them as follows:
importplay.api.libs.json._importplay.api.libs.json.Reads._importplay.api.libs.functional.syntax._implicitvallocationReads: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 _)
implicitvalresidentReads: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 _)
implicitvalplaceReads:Reads[Place] = (
(JsPath\"name").read[String](minLength[String](2)) and
(JsPath\"location").read[Location] and
(JsPath\"residents").read[Seq[Resident]]
)(Place.apply _)
valjson= { ... }
json.validate[Place] match {
cases: JsSuccess[Place] => {
valplace:Place= s.get
// do something with place
}
casee: JsError=> {
// error handling flow
}
}Similarly, I could construct Writes like this:
importplay.api.libs.json._importplay.api.libs.functional.syntax._implicitvallocationWrites:Writes[Location] = (
(JsPath\"lat").write[Double] and
(JsPath\"long").write[Double]
)(unlift(Location.unapply))
implicitvalresidentWrites:Writes[Resident] = (
(JsPath\"name").write[String] and
(JsPath\"age").write[Int] and
(JsPath\"role").writeNullable[String]
)(unlift(Resident.unapply))
implicitvalplaceWrites:Writes[Place] = (
(JsPath\"name").write[String] and
(JsPath\"location").write[Location] and
(JsPath\"residents").write[Seq[Resident]]
)(unlift(Place.unapply))
valplace=Place(
"Watership Down",
Location(51.235685, -1.309197),
Seq(
Resident("Fiver", 4, None),
Resident("Bigwig", 6, Some("Owsla"))
)
)
valjson=Json.toJson(place)It is also possible to implement custom logic by implementing the Reads, Writes and/or Format traits manually, but we recommend using the automatic conversion macros or the functional DSL if possible.
JSON can also be manually constructed using a DSL:
valjson:JsValue=Json.obj(
"name"->"Watership Down",
"location"->Json.obj("lat"->51.235685, "long"->-1.309197),
"residents"->Json.arr(
Json.obj(
"name"->"Fiver",
"age"->4,
"role"->JsNull
),
Json.obj(
"name"->"Bigwig",
"age"->6,
"role"->"Owsla"
)
)
)See https://github.com/playframework/.github/blob/main/RELEASING.md
Play JSON is licensed under the Apache license, version 2. See the LICENSE file for more information.