Scala Language
JSON
Recherche…
JSON avec spray-json
spray-json fournit un moyen facile de travailler avec JSON. En utilisant des formats implicites, tout se passe "dans les coulisses":
Rendez la bibliothèque disponible avec SBT
Pour gérer spray-json
avec des dépendances de bibliothèques gérées par SBT :
libraryDependencies += "io.spray" %% "spray-json" % "1.3.2"
Notez que le dernier paramètre, le numéro de version ( 1.3.2
), peut être différent selon les projets.
La bibliothèque spray-json
est hébergée sur repo.spray.io .
Importer la bibliothèque
import spray.json._
import DefaultJsonProtocol._
Le protocole JSON par défaut DefaultJsonProtocol
contient des formats pour tous les types de base. Pour fournir une fonctionnalité JSON pour les types personnalisés, utilisez des générateurs de convenance pour les formats ou écrivez des formats explicitement.
Lire JSON
// generates an intermediate JSON representation (abstract syntax tree)
val res = """{ "foo": "bar" }""".parseJson // JsValue = {"foo":"bar"}
res.convertTo[Map[String, String]] // Map(foo -> bar)
Ecrire JSON
val values = List("a", "b", "c")
values.toJson.prettyPrint // ["a", "b", "c"]
DSL
DSL n'est pas pris en charge.
Lecture-écriture aux classes de cas
L'exemple suivant montre comment sérialiser un objet de classe de cas au format JSON.
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
// create the formats and provide them implicitly
implicit val addressFormat = jsonFormat2(Address)
implicit val personFormat = jsonFormat2(Person)
// serialize a Person
Person("Fred", Address("Awesome Street 9", "SuperCity"))
val fredJsonString = fred.toJson.prettyPrint
Cela se traduit par le JSON suivant:
{
"name": "Fred",
"address": {
"street": "Awesome Street 9",
"city": "SuperCity"
}
}
Ce JSON peut à son tour être désérialisé dans un objet:
val personRead = fredJsonString.parseJson.convertTo[Person]
//Person(Fred,Address(Awesome Street 9,SuperCity))
Format personnalisé
Ecrivez un JsonFormat
personnalisé si une sérialisation spéciale d'un type est requise. Par exemple, si les noms de champs sont différents dans Scala que dans JSON. Ou, si différents types de béton sont instanciés en fonction de l'entrée.
implicit object BetterPersonFormat extends JsonFormat[Person] {
// deserialization code
override def read(json: JsValue): Person = {
val fields = json.asJsObject("Person object expected").fields
Person(
name = fields("name").convertTo[String],
address = fields("home").convertTo[Address]
)
}
// serialization code
override def write(person: Person): JsValue = JsObject(
"name" -> person.name.toJson,
"home" -> person.address.toJson
)
}
JSON avec Circé
Circé fournit des codecs dérivés à la compilation pour en / decode json dans les classes de cas. Un exemple simple ressemble à ceci:
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
case class User(id: Long, name: String)
val user = User(1, "John Doe")
// {"id":1,"name":"John Doe"}
val json = user.asJson.noSpaces
// Right(User(1L, "John Doe"))
val res: Either[Error, User] = decode[User](json)
JSON avec play-json
play-json utilise des formats implicites comme les autres frameworks json
Dépendance de SBT: libraryDependencies += ""com.typesafe.play" %% "play-json" % "2.4.8"
import play.api.libs.json._
import play.api.libs.functional.syntax._ // if you need DSL
DefaultFormat
contient des formats defaul pour lire / écrire tous les types de base. Pour fournir une fonctionnalité JSON pour vos propres types, vous pouvez utiliser des générateurs de convenance pour les formats ou écrire des formats de manière explicite.
Lire json
// generates an intermediate JSON representation (abstract syntax tree)
val res = Json.parse("""{ "foo": "bar" }""") // JsValue = {"foo":"bar"}
res.as[Map[String, String]] // Map(foo -> bar)
res.validate[Map[String, String]] //JsSuccess(Map(foo -> bar),)
Écris json
val values = List("a", "b", "c")
Json.stringify(Json.toJson(values)) // ["a", "b", "c"]
DSL
val json = parse("""{ "foo": [{"foo": "bar"}]}""")
(json \ "foo").get //Simple path: [{"foo":"bar"}]
(json \\ "foo") //Recursive path:List([{"foo":"bar"}], "bar")
(json \ "foo")(0).get //Index lookup (for JsArrays): {"foo":"bar"}
Comme toujours, préférez la correspondance de modèle avec JsSuccess
/ JsError
et essayez d’éviter les .get
au .get
, array(i)
.
Lire et écrire dans une classe de cas
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
// create the formats and provide them implicitly
implicit val addressFormat = Json.format[Address]
implicit val personFormat = Json.format[Person]
// serialize a Person
val fred = Person("Fred", Address("Awesome Street 9", "SuperCity"))
val fredJsonString = Json.stringify(Json.toJson(Json.toJson(fred)))
val personRead = Json.parse(fredJsonString).as[Person] //Person(Fred,Address(Awesome Street 9,SuperCity))
Format propre
Vous pouvez écrire votre propre JsonFormat si vous avez besoin d'une sérialisation spéciale de votre type (par exemple, nommer les champs différemment dans scala et Json ou instancier différents types de béton en fonction de l'entrée)
case class Address(street: String, city: String)
// create the formats and provide them implicitly
implicit object AddressFormatCustom extends Format[Address] {
def reads(json: JsValue): JsResult[Address] = for {
street <- (json \ "Street").validate[String]
city <- (json \ "City").validate[String]
} yield Address(street, city)
def writes(x: Address): JsValue = Json.obj(
"Street" -> x.street,
"City" -> x.city
)
}
// serialize an address
val address = Address("Awesome Street 9", "SuperCity")
val addressJsonString = Json.stringify(Json.toJson(Json.toJson(address)))
//{"Street":"Awesome Street 9","City":"SuperCity"}
val addressRead = Json.parse(addressJsonString).as[Address]
//Address(Awesome Street 9,SuperCity)
Alternative
Si json ne correspond pas exactement à vos champs de classe de cas ( isAlive
dans la classe de cas vs is_alive
dans json):
case class User(username: String, friends: Int, enemies: Int, isAlive: Boolean)
object User {
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val userReads: Reads[User] = (
(JsPath \ "username").read[String] and
(JsPath \ "friends").read[Int] and
(JsPath \ "enemies").read[Int] and
(JsPath \ "is_alive").read[Boolean]
) (User.apply _)
}
Json avec des champs optionnels
case class User(username: String, friends: Int, enemies: Int, isAlive: Option[Boolean])
object User {
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val userReads: Reads[User] = (
(JsPath \ "username").read[String] and
(JsPath \ "friends").read[Int] and
(JsPath \ "enemies").read[Int] and
(JsPath \ "is_alive").readNullable[Boolean]
) (User.apply _)
}
Lecture des horodatages de json
Imaginez que vous ayez un objet Json, avec un champ d’horodatage Unix:
{
"field": "example field",
"date": 1459014762000
}
Solution:
case class JsonExampleV1(field: String, date: DateTime)
object JsonExampleV1{
implicit val r: Reads[JsonExampleV1] = (
(__ \ "field").read[String] and
(__ \ "date").read[DateTime](Reads.DefaultJodaDateReads)
)(JsonExampleV1.apply _)
}
Lecture de classes de cas personnalisées
Maintenant, si vous encapsulez vos identifiants d'objet pour la sécurité de type, vous apprécierez cela. Voir l'objet json suivant:
{
"id": 91,
"data": "Some data"
}
et les classes de cas correspondantes:
case class MyIdentifier(id: Long)
case class JsonExampleV2(id: MyIdentifier, data: String)
Il ne vous reste plus qu'à lire le type primitif (Long) et à mapper sur votre idenfier:
object JsonExampleV2 {
implicit val r: Reads[JsonExampleV2] = (
(__ \ "id").read[Long].map(MyIdentifier) and
(__ \ "data").read[String]
)(JsonExampleV2.apply _)
}
code à https://github.com/pedrorijo91/scala-play-json-examples
JSON avec json4s
json4s utilise des formats implicites comme les autres frameworks json.
Dépendance à SBT:
libraryDependencies += "org.json4s" %% "json4s-native" % "3.4.0"
//or
libraryDependencies += "org.json4s" %% "json4s-jackson" % "3.4.0"
Importations
import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.native.JsonMethods._
implicit val formats = DefaultFormats
DefaultFormats
contient des formats par défaut pour lire / écrire tous les types de base.
Lire json
// generates an intermediate JSON representation (abstract syntax tree)
val res = parse("""{ "foo": "bar" }""") // JValue = {"foo":"bar"}
res.extract[Map[String, String]] // Map(foo -> bar)
Écris json
val values = List("a", "b", "c")
compact(render(values)) // ["a", "b", "c"]
DSL
json \ "foo" //Simple path: JArray(List(JObject(List((foo,JString(bar))))))
json \\ "foo" //Recursive path: ~List([{"foo":"bar"}], "bar")
(json \ "foo")(0) //Index lookup (for JsArrays): JObject(List((foo,JString(bar))))
("foo" -> "bar") ~ ("field" -> "value") // {"foo":"bar","field":"value"}
Lire et écrire dans une classe de cas
import org.json4s.native.Serialization.{read, write}
case class Address(street: String, city: String)
val addressString = write(Address("Awesome stree", "Super city"))
// {"street":"Awesome stree","city":"Super city"}
read[Address](addressString) // Address(Awesome stree,Super city)
//or
parse(addressString).extract[Address]
Lire et écrire des listes hétérogènes
Pour sérialiser et désérialiser une liste hétérogène (ou polymorphe), des indications de type spécifiques doivent être fournies.
trait Location
case class Street(name: String) extends Location
case class City(name: String, zipcode: String) extends Location
case class Address(street: Street, city: City) extends Location
case class Locations (locations : List[Location])
implicit val formats = Serialization.formats(ShortTypeHints(List(classOf[Street], classOf[City], classOf[Address])))
val locationsString = write(Locations(Street("Lavelle Street"):: City("Super city","74658")))
read[Locations](locationsString)
Format propre
class AddressSerializer extends CustomSerializer[Address](format => (
{
case JObject(JField("Street", JString(s)) :: JField("City", JString(c)) :: Nil) =>
new Address(s, c)
},
{
case x: Address => ("Street" -> x.street) ~ ("City" -> x.city)
}
))
implicit val formats = DefaultFormats + new AddressSerializer
val str = write[Address](Address("Awesome Stree", "Super City"))
// {"Street":"Awesome Stree","City":"Super City"}
read[Address](str)
// Address(Awesome Stree,Super City)