文档

§JSON 自动映射

如果 JSON 直接映射到一个类,我们提供了一个方便的宏,这样您就不必手动编写 Reads[T]Writes[T]Format[T]。给定以下 case class

case class Resident(name: String, age: Int, role: Option[String])

以下宏将根据其结构和字段名称创建 Reads[Resident]

import play.api.libs.json._

implicit val residentReads: Reads[Resident] = Json.reads[Resident]

编译时,宏将检查给定的类并
注入以下代码,就像您手动编写它一样

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val residentReads: Reads[Resident] = (
  (__ \ "name").read[String] and
    (__ \ "age").read[Int] and
    (__ \ "role").readNullable[String]
)(Resident.apply _)

这在编译时完成,因此您不会丢失任何类型安全或性能。
类似的宏存在于 Writes[T]Format[T]

import play.api.libs.json._

implicit val residentWrites: OWrites[Resident] = Json.writes[Resident]
import play.api.libs.json._

implicit val residentFormat: Format[Resident] = Json.format[Resident]

因此,将 case class 自动转换为 JSON 的完整示例如下

import play.api.libs.json._

implicit val residentWrites: OWrites[Resident] = Json.writes[Resident]

val resident = Resident(name = "Fiver", age = 4, role = None)

val residentJson: JsValue = Json.toJson(resident)

将 JSON 自动解析为 case class 的完整示例如下

import play.api.libs.json._

implicit val residentReads: Reads[Resident] = Json.reads[Resident]

// In a request, a JsValue is likely to come from `request.body.asJson`
// or just `request.body` if using the `Action(parse.json)` body parser
val jsonString: JsValue = Json.parse(
  """{
  "name" : "Fiver",
  "age" : 4
}"""
)

val residentFromJson: JsResult[Resident] =
  Json.fromJson[Resident](jsonString)

residentFromJson match {
  case JsSuccess(r: Resident, path: JsPath) =>
    println("Name: " + r.name)

  case e @ JsError(_) =>
    println("Errors: " + JsError.toJson(e).toString())
}

值类 也受支持。给定以下基于 String 值的值类

final class IdText(val value: String) extends AnyVal

然后也可以使用以下宏生成 Reads[IdText](因为 String 已经受支持)

import play.api.libs.json._

implicit val idTextReads: Reads[IdText] = Json.valueReads[IdText]

与 case class 类似,类似的宏存在于 Writes[T]Format[T]

import play.api.libs.json._

implicit val idTextWrites: Writes[IdText] = Json.valueWrites[IdText]
import play.api.libs.json._

implicit val idTextFormat: Format[IdText] = Json.valueFormat[IdText]

注意:要能够从 request.body.asJson 访问 JSON,请求必须具有 Content-Type 标头为 application/json。您可以使用 `tolerantJson` 体解析器 放宽此约束。

上面的示例可以通过使用带有类型化验证函数的体解析器变得更加简洁。请参阅 JSON 与 HTTP 文档中的 savePlaceConcise 示例

§要求

宏适用于满足以下要求的类和特质。

Scala 2.x 中的类

Scala 3.1.x 中的类: (+3.1.2-RC2)

案例类自动满足这些要求。对于自定义类或特质,您可能需要实现它们。

特质也可以支持,当且仅当它是密封的,并且子类型符合之前的要求。

sealed trait Role
case object Admin                            extends Role
case class Contributor(organization: String) extends Role

密封族实例的 JSON 表示包含一个鉴别器字段,它指定有效的子类型(一个文本字段,默认名称为_type)。

val adminJson = Json.parse("""
  { "_type": "scalaguide.json.ScalaJsonAutomatedSpec.Admin" }
""")

val contributorJson = Json.parse("""
  {
    "_type":"scalaguide.json.ScalaJsonAutomatedSpec.Contributor",
    "organization":"Foo"
  }
""")

// Each JSON objects is marked with the _type,
// indicating the fully-qualified name of sub-type

然后宏能够生成Reads[T]OWrites[T]OFormat[T]

import play.api.libs.json._

// First provide instance for each sub-types 'Admin' and 'Contributor':
implicit val adminFormat = OFormat[Admin.type](Reads[Admin.type] {
  case JsObject(_) => JsSuccess(Admin)
  case _           => JsError("Empty object expected")
}, OWrites[Admin.type] { _ =>
  Json.obj()
})

implicit val contributorFormat: OFormat[Contributor] = Json.format[Contributor]

// Finally able to generate format for the sealed family 'Role'
implicit val roleFormat: OFormat[Role] = Json.format[Role]

§自定义命名策略

要使用自定义命名策略,您需要定义一个隐式JsonConfiguration对象和一个JsonNaming

提供两种命名策略:默认策略,使用类属性的名称原样;
以及JsonNaming.SnakeCase案例策略。

可以使用以下方法使用除默认策略之外的策略

import play.api.libs.json._

implicit val config: JsonConfiguration = JsonConfiguration(SnakeCase)

implicit val userReads: Reads[PlayUser] = Json.reads[PlayUser]
import play.api.libs.json._

implicit val config: JsonConfiguration = JsonConfiguration(SnakeCase)

implicit val userWrites: OWrites[PlayUser] = Json.writes[PlayUser]
import play.api.libs.json._

implicit val config: JsonConfiguration = JsonConfiguration(SnakeCase)

implicit val userFormat: OFormat[PlayUser] = Json.format[PlayUser]

特质表示也可以配置,使用自定义的鉴别器字段名称或子类型名称作为该字段值的编码方式。

val adminJson = Json.parse("""
  { "admTpe": "admin" }
""")

val contributorJson = Json.parse("""
  {
    "admTpe":"contributor",
    "organization":"Foo"
  }
""")

为此,可以在解析的JsonConfiguration中定义设置discriminatortypeNaming

import play.api.libs.json._

implicit val cfg: JsonConfiguration = JsonConfiguration(
  // Each JSON objects is marked with the admTpe, ...
  discriminator = "admTpe",
  // ... indicating the lower-cased name of sub-type
  typeNaming = JsonNaming { fullName =>
    fullName.drop(39 /* remove pkg */ ).toLowerCase
  }
)

// First provide instance for each sub-types 'Admin' and 'Contributor':
implicit val adminFormat = OFormat[Admin.type](Reads[Admin.type] {
  case JsObject(_) => JsSuccess(Admin)
  case _           => JsError("Empty object expected")
}, OWrites[Admin.type] { _ =>
  Json.obj()
})

implicit val contributorFormat: OFormat[Contributor] = Json.format[Contributor]

// Finally able to generate format for the sealed family 'Role'
implicit val roleFormat: OFormat[Role] = Json.format[Role]

§实现您自己的命名策略

要实现您自己的命名策略,您只需要实现JsonNaming特质。

import play.api.libs.json._

object OpenCollective extends JsonNaming {
  override def apply(property: String): String = s"opencollective_$property"
}

implicit val config: JsonConfiguration = JsonConfiguration(OpenCollective)

implicit val customWrites: OFormat[PlayUser] = Json.format[PlayUser]

§自定义宏以输出 null

可以配置宏以在 Json 中输出null值,而不是删除空字段。

import play.api.libs.json._

implicit val config: JsonConfiguration         = JsonConfiguration(optionHandlers = OptionHandlers.WritesNull)
implicit val residentWrites: OWrites[Resident] = Json.writes[Resident]

下一步:JSON 变换器


发现此文档中的错误?此页面的源代码可以在此处找到。阅读文档指南后,请随时贡献拉取请求。有疑问或建议要分享?前往我们的社区论坛与社区开始对话。