Scala Toolkit

如何將 JSON 反序列化為物件?

語言

使用 Scala CLI,您可以在單一行中要求整個 Toolkit

//> using toolkit latest

或者,您也可以只要求特定版本的 UPickle

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

在您的 build.sbt 檔案中,您可以新增對 Toolkit 的依賴項

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 class 產生 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

此頁面的貢獻者