Scala Language
JSON
Zoeken…
JSON met spray-json
spray-json biedt een eenvoudige manier om met JSON te werken. Met impliciete formaten gebeurt alles "achter de schermen":
Maak de bibliotheek beschikbaar met SBT
spray-json
beheren met SBT-beheerde bibliotheekafhankelijkheden :
libraryDependencies += "io.spray" %% "spray-json" % "1.3.2"
Merk op dat de laatste parameter, het versienummer ( 1.3.2
), in verschillende projecten anders kan zijn.
De spray-json
bibliotheek wordt gehost op repo.spray.io .
Importeer de bibliotheek
import spray.json._
import DefaultJsonProtocol._
Het standaard JSON-protocol DefaultJsonProtocol
bevat indelingen voor alle basistypen. Om JSON-functionaliteit voor aangepaste typen te bieden, gebruikt u gemaksmakers voor indelingen of schrijft u indelingen expliciet.
Lees 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)
Schrijf JSON
val values = List("a", "b", "c")
values.toJson.prettyPrint // ["a", "b", "c"]
DSL
DSL wordt niet ondersteund.
Lezen en schrijven naar casusklassen
Het volgende voorbeeld laat zien hoe een case-klasseobject in de JSON-indeling moet worden geserialiseerd.
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
Dit resulteert in de volgende JSON:
{
"name": "Fred",
"address": {
"street": "Awesome Street 9",
"city": "SuperCity"
}
}
Dat JSON op zijn beurt weer kan worden gedeserialiseerd in een object:
val personRead = fredJsonString.parseJson.convertTo[Person]
//Person(Fred,Address(Awesome Street 9,SuperCity))
Aangepast formaat
Schrijf een aangepast JsonFormat
als een speciale serialisatie van een type vereist is. Als de veldnamen bijvoorbeeld in Scala anders zijn dan in JSON. Of, als verschillende betonsoorten worden geconcretiseerd op basis van de invoer.
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 met Circe
Circe biedt compilatie-tijd afgeleide codecs voor en / decoderen json in case-klassen. Een eenvoudig voorbeeld ziet er zo uit:
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 met play-json
play-json gebruikt impliciete formaten als andere json-frameworks
SBT-afhankelijkheid: 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
bevat standaardformaten om alle basistypes te lezen / schrijven. Om JSON-functionaliteit voor uw eigen typen te bieden, kunt u ofwel gemaksmakers voor formaten gebruiken of expliciet formaten schrijven.
Lees 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),)
Schrijf 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"}
Zoals altijd de voorkeur aan patroonherkenning tegen JsSuccess
/ JsError
en probeer te voorkomen .get
, array(i)
gesprekken.
Lees en schrijf naar case-klasse
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))
Eigen indeling
U kunt uw eigen JsonFormat schrijven als u een speciale serialisatie van uw type nodig hebt (bijvoorbeeld de velden anders in scala en Json anders benoemen of verschillende betonsoorten instantiëren op basis van de invoer)
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)
Alternatief
Als de json niet exact overeenkomt met de velden van uw isAlive
( isAlive
in case class versus is_alive
in 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 met optionele velden
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 _)
}
Tijdstempels lezen van json
Stel je voor dat je een Json-object hebt, met een Unix-tijdstempelveld:
{
"field": "example field",
"date": 1459014762000
}
oplossing:
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 _)
}
Aangepaste case-klassen lezen
Nu, als u uw object-ID's voor typeveiligheid inpakt, zult u hiervan genieten. Zie het volgende json-object:
{
"id": 91,
"data": "Some data"
}
en de bijbehorende case-klassen:
case class MyIdentifier(id: Long)
case class JsonExampleV2(id: MyIdentifier, data: String)
Nu hoeft u alleen het primitieve type (lang) te lezen en toe te wijzen aan uw identiteit:
object JsonExampleV2 {
implicit val r: Reads[JsonExampleV2] = (
(__ \ "id").read[Long].map(MyIdentifier) and
(__ \ "data").read[String]
)(JsonExampleV2.apply _)
}
code op https://github.com/pedrorijo91/scala-play-json-examples
JSON met json4s
json4s gebruikt impliciete formaten als andere json-frameworks.
SBT-afhankelijkheid:
libraryDependencies += "org.json4s" %% "json4s-native" % "3.4.0"
//or
libraryDependencies += "org.json4s" %% "json4s-jackson" % "3.4.0"
invoer
import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.native.JsonMethods._
implicit val formats = DefaultFormats
DefaultFormats
bevat standaardindelingen om alle basistypes te lezen / schrijven.
Lees 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)
Schrijf 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"}
Lees en schrijf naar case-klasse
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]
Lees en schrijf heterogene lijsten
Om een heterogene (of polymorfe) lijst te serialiseren en te deserialiseren, moeten specifieke type-hints worden verstrekt.
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)
Eigen indeling
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)