Scala 工具包

如何将 JSON 反序列化为对象?

语言

使用 Scala CLI,你可以在一行中请求整个工具包

//> using toolkit latest

或者,你只需请求特定版本的 UPickle

//> using dep com.lihaoyi::upickle:3.1.0

在你的 build.sbt 文件中,你可以添加对工具包的依赖项

lazy val example = project.in(file("example"))
  .settings(
    scalaVersion := "3.2.2",
    libraryDependencies += "org.scala-lang" %% "toolkit" % "0.1.7"
  )

或者,你只需请求特定版本的 UPickle

libraryDependencies += "com.lihaoyi" %% "upickle" % "3.1.0"

在你的 build.sc 文件中,你可以添加对 upickle 库的依赖项

object example extends ScalaModule {
  def scalaVersion = "3.2.2"
  def ivyDeps =
    Agg(
      ivy"org.scala-lang::toolkit:0.1.7"
    )
}

或者,你只需请求特定版本的 UPickle

ivy"com.lihaoyi::upickle:3.1.0"

解析与反序列化

使用 uJson 解析只接受有效的 JSON,但它不会验证字段的名称和类型是否符合预期。

另一方面,反序列化将 JSON 字符串转换为某些用户指定的 Scala 数据类型,如果必需的字段存在且具有正确的类型。

在本教程中,我们将展示如何反序列化为 Map,以及如何反序列化为自定义 case class

将 JSON 反序列化为 Map

对于类型 T,uPickle 可以将 JSON 反序列化为 Map[String, T],检查所有字段是否符合 T

例如,我们可以反序列化到 Map[String, List[Int]]

val json = """{"primes": [2, 3, 5], "evens": [2, 4, 6]} """
val map: Map[String, List[Int]] =
  upickle.default.read[Map[String, List[Int]]](json)

println(map("primes"))
// prints: List(2, 3, 5)

如果某个值类型错误,uPickle 会抛出 upickle.core.AbortException

val json = """{"name": "Peter"} """
upickle.default.read[Map[String, List[Int]]](json)
// throws: upickle.core.AbortException: expected sequence got string at index 9

将 JSON 反序列化到自定义数据类型

在 Scala 中,你可以使用 case class 来定义你自己的数据类型。例如,要表示宠物主人,你可以

case class PetOwner(name: String, pets: List[String])

要从 JSON 读取 PetOwner,我们必须提供 ReadWriter[PetOwner]。uPickle 可以自动完成此操作

import upickle.default._

implicit val ownerRw: ReadWriter[PetOwner] = macroRW[PetOwner]

一些解释

  • implicit val 是一个值,可以自动作为方法或函数调用的参数提供,而无需显式传递它。
  • macroRW 是 uPickle 提供的一个方法,它可以利用 case 类的字段信息生成 ReadWriter 的实例。
import upickle.default.*

case class PetOwner(name: String, pets: List[String])
  derives ReadWriter

derives 关键字用于自动生成给定实例。它利用编译器对 PetOwner 中字段的了解,生成 ReadWriter[PetOwner]

这意味着你现在可以使用 upickle.default.read(petOwner) 从 JSON 中读取(和写入)PetOwner 对象。

请注意,你无需将 ReadWriter[PetOwner] 的实例显式传递给 read 方法。但它仍然可以从上下文中获取它,作为“给定”值。你可以在 Scala 3 Book 中找到有关上下文抽象的更多信息。

将所有内容放在一起,你应该得到

import upickle.default._

case class PetOwner(name: String, pets: List[String])
implicit val ownerRw: ReadWriter[PetOwner] = macroRW

val json = """{"name": "Peter", "pets": ["Toolkitty", "Scaniel"]}"""
val petOwner: PetOwner = read[PetOwner](json)

val firstPet = petOwner.pets.head
println(s"${petOwner.name} has a pet called $firstPet")
// prints: Peter has a pet called Toolkitty
import upickle.default.*

case class PetOwner(name: String, pets: List[String]) derives ReadWriter

val json = """{"name": "Peter", "pets": ["Toolkitty", "Scaniel"]}"""
val petOwner: PetOwner = read[PetOwner](json)

val firstPet = petOwner.pets.head
println(s"${petOwner.name} has a pet called $firstPet")
// prints: Peter has a pet called Toolkitty

本页面的贡献者