您正在使用 Scala 进行理解,我相信很多困惑是关于理解如何工作的。这是 Scala 语法,用于以简洁的方式访问 monad 的 map、flatMap 和 filter 方法以迭代集合。为了完全理解这一点,您将需要对 monad 和理解有所了解。Scala 文档可以提供帮助,搜索“scala for comprehension”也可以提供帮助。您还需要了解 Scala 中的提取器。
您询问了这一行的含义:
JObject(rec) <- json \ "records"
这是理解的一部分。
您的声明:
我知道 json \"records" 会产生一个 JArray 对象,
有点不正确。\ 函数List[JSObject]
从解析器结果中提取 a,json
但是为什么它在 <- 的左侧被提取为 JObject(rec)?
使用json \ "records"
json4s 提取器 \ 来选择 Json 数据的“记录”成员并生成一个List[JObject]
. 可以读作“<-
取自”并暗示您正在遍历列表。列表的元素具有 JObject 类型,并且构造JObject(rec)
应用提取器来创建一个值 ,rec
该值包含 JObject 的内容(其字段)。
为什么它在 <- 的左侧被提取为 JObject(rec)?
这就是迭代集合的 Scala 语法。例如,我们也可以这样写:
for (x <- 1 to 10)
这只会给我们从 1 到 10 in 的值x
。在您的示例中,我们使用了类似类型的迭代,但覆盖了 JObjects 列表的内容。
JObject(rec) 的含义是什么?
这是一个 Scala 提取器。如果您查看 json4s 代码,您会发现 JObject 是这样定义的:
case class JObject(obj: List[JField]) extends JValue
当我们在 Scala 中有一个案例类时,会自动定义两个方法:apply 和 unapply。then的意思JObject(rec)
是调用 unapply 方法并产生一个值 ,rec
它对应obj
于 JObject 构造函数(apply 方法)中的值。所以,rec
会有类型List[JField]
。
rec 变量从何而来?
它来自于简单地使用它,并被声明为obj
JObject 的 apply 方法的参数的占位符。
JObject(rec) 是否意味着从 rec 输入实例化新的 JObject 类?
不,它没有。它的出现是因为 JArrayjson \ "records"
仅包含 JObject 值。
所以,解释一下:
JObject(rec) <- json \ "records"
我们可以用英文编写以下伪代码:
在解析的 json 中找到作为 JArray 的“记录”并对其进行迭代。JArray 的元素应该是 JObject 类型。将每个 JObject 的“obj”字段作为 JField 列表拉出,并将其分配给名为“rec”的值。
希望这能让这一切变得更清楚吗?
如果您可以向我展示上面循环的 Java 等效代码,这也很有帮助。
当然,这是可以做到的,但这比我愿意在这里做出的贡献要多得多。您可以做的一件事是使用 Scala 编译代码,找到相关的 .class 文件,然后将它们反编译为 Java。这可能对您了解 Scala 在多大程度上简化了 Java 编程很有指导意义。:)
为什么我不能这样做?for ( rec <- json \ "records",所以 rec 变成了 JObject。<- 左边的 JObject(rec) 是什么原因?
你可以!但是,您随后需要获取 JObject 的内容。你可以这样写 for 理解:
val records: List[Map[String, Any]] = for {
obj: JObject <- json \ "records"
rec = obj.obj
JField("name", JString(name)) <- rec
JField("address", JString(address)) <- rec
} yield Map("name" -> name, "address" -> address)
它具有相同的含义,但更长。
我只是想了解 N(x) 模式是什么意思,因为我以前只见过 for (x <- y 模式。
如上所述,这是一个提取器,它只是使用为案例类自动创建的 unapply 方法。在 Scala 的 case 语句中也做了类似的事情。
更新:
您提供的代码无法针对 json4s-native 的 3.2.11 版本为我编译。这个导入:
import org.json4s.JsonAST._
此导入是多余的:
import org.json4s._
这样 JObject 被定义了两次。如果我删除 JsonAST 导入,那么它编译得很好。
为了进一步测试这一点,我将您的代码放在这样的 scala 文件中:
package example
import org.json4s._
// import org.json4s.JsonAST._
import org.json4s.native.JsonParser
class ForComprehension {
val json = JsonParser.parse(
"""{
|"records":[
|{"name":"John Derp","address":"Jem Street 21"},
|{"name":"Scala Jo","address":"in my sweet dream"}
|]}""".stripMargin
)
val records: List[Map[String, Any]] = for {
JObject(rec) <- json \ "records"
JField("name", JString(name)) <- rec
JField("address", JString(address)) <- rec
} yield Map("name" -> name, "address" -> address)
println(records)
}
然后开始一个 Scala REPL 会话来调查:
scala> import example.ForComprehension
import example.ForComprehension
scala> val x = new ForComprehension
List(Map(name -> John Derp, address -> Jem Street 21), Map(name -> Scala Jo, address -> in my sweet dream))
x: example.ForComprehension = example.ForComprehension@5f9cbb71
scala> val obj = x.json \ "records"
obj: org.json4s.JValue = JArray(List(JObject(List((name,JString(John Derp)), (address,JString(Jem Street 21)))), JObject(List((name,JString(Scala Jo)), (address,JString(in my sweet dream))))))
scala> for (a <- obj) yield { a }
res1: org.json4s.JValue = JArray(List(JObject(List((name,JString(John Derp)), (address,JString(Jem Street 21)))), JObject(List((name,JString(Scala Jo)), (address,JString(in my sweet dream))))))
scala> import org.json4s.JsonAST.JObject
for ( JObject(rec) <- obj ) yield { rec }
import org.json4s.JsonAST.JObject
scala> res2: List[List[org.json4s.JsonAST.JField]] = List(List((name,JString(John Derp)), (address,JString(Jem Street 21))), List((name,JString(Scala Jo)), (address,JString(in my sweet dream))))
所以:
- 你是对的, \ 运算符的结果是一个 JArray
- JArray 上的“迭代”只是将整个数组视为列表中的唯一值
- 必须存在从 JArray 到 JObject 的隐式转换,以允许提取器将 JArray 的内容作为 List[JField] 产生。
- 一旦一切都是列表,for 理解就会正常进行。
希望对您理解这一点有所帮助。
有关分配中模式匹配的更多信息,请尝试此博客
更新#2:我挖掘了更多发现这里的隐式转换。罪魁祸首是 \ 运算符。要了解如何json \ "records"
变成单子可迭代事物,您必须查看以下代码:
因此,本质上,使用 \ 运算符会导致以下操作序列: - 将 json (JValue) 隐式转换为 MonadicJValue - 在 MonadicJValue 中应用 \ 运算符以产生 JArray(“记录”) - 隐式转换 JArray into MonadicJValue - 使用 MonadicJValue.filter 和 MonadicJValue.map 方法来实现 for 理解