§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 中的类
- 它必须有一个伴生对象,包含
apply
和unapply
方法。 unapply
的返回值类型必须与apply
方法的参数类型匹配。apply
方法的参数名称必须与 JSON 中所需的属性名称相同。
Scala 3.1.x 中的类: (+3.1.2-RC2)
- 它必须提供一个到
_ <: Product
的Conversion
。 - 它必须提供一个有效的
ProductOf
。
案例类自动满足这些要求。对于自定义类或特质,您可能需要实现它们。
特质也可以支持,当且仅当它是密封的,并且子类型符合之前的要求。
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
中定义设置discriminator
和typeNaming
。
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 变换器
发现此文档中的错误?此页面的源代码可以在此处找到。阅读文档指南后,请随时贡献拉取请求。有疑问或建议要分享?前往我们的社区论坛与社区开始对话。