使用 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