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)


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow