Scala Language
JSON
Поиск…
JSON с распылителем
spray-json обеспечивает простой способ работы с JSON. Используя неявные форматы, все происходит «за кулисами»:
Сделать библиотеку доступной с SBT
Чтобы управлять spray-json
с зависимостями управляемой библиотеки SBT :
libraryDependencies += "io.spray" %% "spray-json" % "1.3.2"
Обратите внимание, что последний параметр, номер версии ( 1.3.2
), может отличаться в разных проектах.
Библиотека spray-json
размещена на сайте repo.spray.io .
Импорт библиотеки
import spray.json._
import DefaultJsonProtocol._
По умолчанию протокол JSON DefaultJsonProtocol
содержит форматы для всех основных типов. Чтобы обеспечить функциональность JSON для пользовательских типов, либо используйте конструкторы удобства для форматов, либо форматы записи явно.
Читать 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)
Написать JSON
val values = List("a", "b", "c")
values.toJson.prettyPrint // ["a", "b", "c"]
DSL
DSL не поддерживается.
Чтение-запись в класс Case
В следующем примере показано, как сериализовать объект класса case в формате 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
Это приводит к следующему JSON:
{
"name": "Fred",
"address": {
"street": "Awesome Street 9",
"city": "SuperCity"
}
}
То, что JSON может, в свою очередь, десериализоваться обратно в объект:
val personRead = fredJsonString.parseJson.convertTo[Person]
//Person(Fred,Address(Awesome Street 9,SuperCity))
Пользовательский формат
Напишите пользовательский JsonFormat
если требуется специальная сериализация типа. Например, если имена полей в Scala различны, чем в JSON. Или, если различные конкретные типы создаются на основе ввода.
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 с Circe
Circe предоставляет кодеки, производные от компиляции, для en / decode json в классах классов. Простой пример выглядит следующим образом:
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 с play-json
play-json использует неявные форматы в качестве других json-фреймворков
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
содержит форматы defaul для чтения / записи всех основных типов. Чтобы обеспечить функциональность JSON для ваших собственных типов, вы можете явно использовать конструкторы удобства для форматов или форматов записи.
Читать 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),)
Написать 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"}
Как всегда предпочитайте сопоставление шаблонов с JsSuccess
/ JsError
и старайтесь избегать .get
, array(i)
.
Чтение и запись в класс case
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))
Собственный формат
Вы можете написать свой собственный JsonFormat, если вам нужна специальная сериализация вашего типа (например, называть поля по-разному в scala и Json или создавать экземпляры разных конкретных типов на основе ввода)
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)
альтернатива
Если json точно не соответствует вашим полям класса case ( isAlive
в случае класса vs is_alive
в 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 с дополнительными полями
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 _)
}
Чтение временных меток от json
Представьте, что у вас есть объект Json с полем отметки Unix:
{
"field": "example field",
"date": 1459014762000
}
решение:
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 _)
}
Чтение пользовательских классов
Теперь, если вы завернете идентификаторы объектов для безопасности типов, вам это понравится. См. Следующий объект json:
{
"id": 91,
"data": "Some data"
}
и соответствующие классы случаев:
case class MyIdentifier(id: Long)
case class JsonExampleV2(id: MyIdentifier, data: String)
Теперь вам просто нужно прочитать примитивный тип (Long) и сопоставить с вашим idenfier:
object JsonExampleV2 {
implicit val r: Reads[JsonExampleV2] = (
(__ \ "id").read[Long].map(MyIdentifier) and
(__ \ "data").read[String]
)(JsonExampleV2.apply _)
}
код на странице https://github.com/pedrorijo91/scala-play-json-examples
JSON с json4s
json4s использует неявные форматы в качестве других json-фреймворков.
Зависимость SBT:
libraryDependencies += "org.json4s" %% "json4s-native" % "3.4.0"
//or
libraryDependencies += "org.json4s" %% "json4s-jackson" % "3.4.0"
импорт
import org.json4s.JsonDSL._
import org.json4s._
import org.json4s.native.JsonMethods._
implicit val formats = DefaultFormats
DefaultFormats
содержит форматы по умолчанию для чтения / записи всех основных типов.
Читать 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)
Написать 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"}
Чтение и запись в класс case
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]
Чтение и запись гетерогенных списков
Для сериализации и десериализации гетерогенного (или полиморфного) списка необходимо указать конкретные подсказки типа.
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)
Собственный формат
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)