我正在学习Json4s库。
我有一个像这样的json片段:
1 2 3 4 5 6 7 8 9 10 11 12
| {
"records":[
{
"name":"John Derp",
"address":"Jem Street 21"
},
{
"name":"Scala Jo",
"address":"in my sweet dream"
}
]
} |
而且,我有Scala代码,它将json字符串转换为Maps列表,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import org. json4s. _
import org. json4s. JsonAST. _
import org. json4s. native. JsonParser
val json = JsonParser. parse("""{"records ":[{"name ":"John Derp ","address ":"Jem Street 21"},{"name ":"Scala Jo ","address ":"in my sweet dream "}]}""")
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 ) |
屏幕上records的输出显示:
List(Map(name -> John Derp, address -> Jem Street 21), Map(name ->
Scala Jo, address -> in my sweet dream))
我想了解for循环中的行的含义。 例如,此行的含义是什么:
1
| JObject(rec) <- json "records" |
我知道json \"records"会生成一个JArray对象,但是为什么在<-的左侧将其提取为JObject(rec)? JObject(rec)语法的含义是什么? rec变量来自哪里? JObject(rec)是否意味着要从rec输入实例化一个新的JObject类?
顺便说一句,我有Java编程背景,因此如果您可以向我展示上述循环的Java等效代码,这也将有所帮助。
-
它产生JObject的JArray。因此,当<-遍历它时,可以将JObject的内容提取到rec变量中。
-
@GborBakos:但是我真的不明白它怎么可能。因为JArray不是List或可迭代对象。另外,为什么我不能这样做? for ( rec <- json \"records",因此rec变为JObject。 <-左侧的JObject(rec)是什??么原因?
-
我不得不承认还没有检查json4s源。我认为JObject(rec)是unapply方法的结果,因此在其中声明了rec。如果JArray具有flatMap方法,则应该可以理解。抱歉,我还没有足够的json4s经验来回答您的问题。我希望其他人可以给您正确的答案。
-
@GborBakos:这是源代码,JArray仅具有values和apply方法。看来您已经了解for (N(x) <- y模式(假设N是一类)。你能解释一下这个模式吗?我只想了解N(x)模式的含义,因为我以前只看过for (x <- y模式。
-
此处:github.com/json4s/json4s/blob/scala_2.10/ast/src/main/scala/org/ JObject是case class,它会自动获取unapply方法,因此可用于模式匹配/提取器,就像在变量声明中一样。请参阅:不耐烦的Scala 14.8。
-
@GborBakos:谢谢。如果未提供实现,默认的unapply方法将采用什么并返回?
-
让我们继续聊天中的讨论。
您具有以下类型层次结构:
您的JSON解析器返回以下对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| object JsonParser {
def parse (s : String ): JValue = {
new JValue {
override def \ (nameToFind : String ): JValue =
JArray (List (
JObject (List (
JField ("name", JString ("John Derp")),
JField ("address", JString ("Jem Street 21")))),
JObject (List (
JField ("name", JString ("Scala Jo")),
JField ("address", JString ("in my sweet dream"))))))
}
}
}
val json = JsonParser. parse("Your JSON") |
在后台,Scala编译器生成以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| val res = (json "records")
. filter(_. isInstanceOf[JObject ])
. flatMap { x =>
x match {
case JObject (obj ) => //
obj //
. withFilter(f => f match {
case JField ("name", _) => true
case _ => false
}) //
. flatMap(n => obj. withFilter(f => f match {
case JField ("address", _) => true
case _ => false
}). map(a => Map (
"name" - > (n. value match { case JString (name ) => name }),
"address" - > (a. value match { case JString (address ) => address }))))
}
} |
第一行JObject(rec) <- json \"records"是可能的,因为JArray.filter返回List[JValue](即List[JObject])。在这里,每个List[JValue]值都通过模式匹配映射到JObject(rec)。
其余调用是一系列带有模式匹配的flatMap和map(这是用于理解的Scala的工作方式)。
我使用了Scala 2.11.4。
当然,上面的match表达式是使用一系列类型检查和强制转换来实现的。
更新:
使用Json4s库时,从JValue到org.json4s.MonadicJValue会有隐式转换。参见package object json4s:
在此使用此转换:JObject(rec) <- json \"records"。首先,将json转换为MonadicJValue,然后应用def \("records"),然后对def \的结果JValue使用def filter,然后再次将其隐式转换为MonadicJValue,然后使用MonadicJValue中的def filter。 MonadicJValue.filter的结果是List[JValue]。之后,执行上述步骤。
-
等待一秒钟,在JObject(rec) <- json \"records"中,通过JArray.filter方法将json \"records"中的JArray转换为List[JObject]。那怎么可能呢?因为当我在RPEL上打印json \"records"时,我得到了以下结果: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))))))。您会看到它返回了JArray,而不是List[JObject]。那么它怎么可能变成List[JObject]?
-
我认为在suuds示例中没有if,因此(filter的withFilter)不是将JValue转换为List[JObject]的方法。
-
仅在for理解中在JArray上调用方法filter,并使用其结果List[JValue]。
-
但是,如果您查看源代码,则JArray JValue没有filter方法。那么如何在for循环中进行迭代呢?
-
JArray扩展了JValue(scala-tools.org/mvnsites/liftweb-2.2/framework/scaladocs/net/)。它包含filter方法。它是Lift Json API。
-
但是我导入了org.json4s.JsonAST._,而不是导入了具有不同程序包的lift-json,并且我检查了是否在sbt中放入了"org.json4s" %%"json4s-native" %"3.2.11",所以它不是我使用的lift-json。
-
另外,我只是再次检查源代码,在JValueJArray类中也没有def \ 方法。这真是奇怪。
-
>>但是,如果您查看源代码,则JArray / JValue没有过滤器方法。 (请参阅我的更新)。
-
>>另外,我只是再次检查源代码,在JValue / JArray类中也没有def 方法。 (org.json4s.MonadicJValue具有def \. Your json`对象被隐式转换为MonadicJValue which has def`和def filter)。
-
好的,我在源代码中发现了这一点,但是什么触发了jvalue2monadic隐式方法被调用?我看到jvalue2extractable隐式方法也接受JValue输入,那么调用jvalue2monadic而不是jvalue2extractable的原因是什么?
-
org.json4s.ExtractableJsonAstNode(jvalue2extractable的结果)没有def ,因此使用org.json4s.MonadicJValue。
-
看到我的更新与解释。
-
嗨,我想知道您是否可以回答这个问题:是否可以将for循环中的行修改为不使用模式匹配?因此,这意味着仅使用简单的x <- y模式而不是JObject(x) <- y。我很好奇,因为rspencer说了它的可能(请参见他的答案中的why I cant do this? for ( rec <- json \"records"..部分)。
-
实际上,您对scalas编译器输出的解释有些错误,因为JValue实际上没有flatMap
-
>>实际上,您对scalas编译器输出的解释有些错误(但是List[JValue]有。您可以反编译问题代码并查看会发生什么)。
您正在使用Scala进行理解,我相信很多困惑是关于理解如何工作的。这是Scala语法,用于以简洁的方式访问集合的monad的map,flatMap和filter方法。您需要对单子和理解有一些了解才能完全理解。 Scala文档可以提供帮助,搜索" scala for comprehension"也可以提供帮助。您还需要了解Scala中的提取器。
好。
您询问了此行的含义:
好。
1
| JObject(rec) <- json "records" |
这是理解的一部分。
好。
您的声明:
好。
I understand that the json \"records" produces a JArray object,
Ok.
有点不正确。 函数从解析器结果json中提取List[JSObject]
好。
but why is it fetched as JObject(rec) at left of <-?
Ok.
json \"records"使用json4s提取器选择Json数据的" records"成员并生成List[JObject]。 <-可以理解为"是从...获取的",表示您正在遍历列表。列表中的元素具有JObject类型,构造JObject(rec)应用提取器来创建值rec,该值保存JObject的内容(其字段)。
好。
how come it's fetched as JObject(rec) at left of <-?
Ok.
这是用于遍历集合的Scala语法。例如,我们还可以编写:
好。
这将简单地为我们提供x中1到10的值。在您的示例中,我们在JObjects列表的内容上使用了类似的迭代方式。
好。
What is the meaning of the JObject(rec)?
Ok.
这是一个Scala提取器。如果您查看json4s代码,则会发现JObject的定义如下:
好。
当我们在Scala中有一个case类时,会自动定义两个方法:apply和unapply。然后,JObject(rec)的含义是调用unapply方法并产生一个值rec,该值与JObject构造函数(应用方法)中的值obj相对应。因此,rec将具有类型List[JField]。
好。
Where does the rec variable come from?
Ok.
它来自简单的使用,并声明为JObject的apply方法的obj参数的占位符。
好。
Does JObject(rec) mean instantiating new JObject class from rec input?
Ok.
不,不是。之所以这样,是因为json \"records"生成的JArray仅包含JObject值。
好。
因此,要解释一下:
好。
1
| JObject(rec) <- json "records" |
我们可以用英语写下面的伪代码:
好。
Find the"records" in the parsed json as a JArray and iterate over them. The elements of the JArray should be of type JObject. Pull the"obj" field of each JObject as a list of JField and assign it to a value named"rec".
Ok.
希望这使所有这些变得更清楚了吗?
好。
it's also helpful if you can show me the Java equivalent code for the loop above.
Ok.
当然可以做到这一点,但这远远超出了我在此所做的贡献。您可以做的一件事是使用Scala编译代码,找到关联的.class文件,然后将其反编译为Java。这对于您了解Scala在Java上简化编程有多大启发。 :)
好。
why I can't do this? for ( rec <- json \"records", so rec become JObject. What is the reason of JObject(rec) at the left of <- ?
Ok.
你可以!但是,您随后需要获取JObject的内容。您可以通过以下方式编写for理解:
好。
1 2 3 4 5 6
| 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 ) |
它的含义相同,但是更长。
好。
I just want to understand what does the N(x) pattern mean, because I only ever see for (x <- y pattern before.
Ok.
如上所述,这是一个提取器,仅使用为案例类自动创建的unapply方法。在Scala中的case语句中完成了类似的操作。
好。
更新:
您提供的代码无法针对json4s-native的3.2.11版本进行编译。此导入:
好。
此导入是多余的:
好。
这样,JObject被定义了两次。如果我删除了JsonAST导入,则可以正常编译。
好。
为了进一步测试,我将您的代码放在这样的scala文件中:
好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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会话以进行调查:
好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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"如何变成单子可迭代的事物,您必须查看以下代码:
好。
org.json4s包对象:此行声明从JValue到MonadicJValue的隐式转换。 那么什么是MonadicJValue?
org.json4s.MonadicJValue:这定义了所有使JValues可迭代以进行理解的东西:filter,map,flatMap,还提供了和\类似XPath的运算符
好。
因此,本质上,使用运算符会导致以下操作序列:
-将json(JValue)隐式转换为MonadicJValue
-在MonadicJValue中应用运算符以产生JArray("记录")
-将JArray隐式转换为MonadicJValue
-使用MonadicJValue.filter和MonadicJValue.map方法来实现for理解
好。
好。
-
感谢您的全面答复。但是我仍然不能在json \"records"的一部分上得到它,因为它返回了JArray而不是List[JObject]。 AFAIK,JArray不是集合,那么怎么可能通过<-对其进行迭代?
-
(我和Suud怀疑org.json4s.jvalue2monadic(github.com/json4s/json4s/blob/scala_2.10/core/src/main/scala/)起作用了,但似乎是其他隐式导致的)。
-
我相信只会为数组返回List [JValue],就像会为对象返回Map [String,JValue]。我更新了答案以反映这一点。
-
但是,如果在RPEL上输入json \"records",它将给出如下输出: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))))))。因此您可以看到json \"records"是JArray。如何将其转换为List[JObject]?
-
我与REPL进行了调查。看到我的答案更新。
-
感谢更新。我仍然想知道是什么使JArray转换为List [JObject]。那就是现在唯一缺少的一块。另一个答案是关于filter的,但是我在源代码的JValue类中找不到任何filter方法。
-
@suud-我添加了另一个更新来解释显式转换。
-
感谢您更新的答案:)。多数民众赞成在非常清楚的解释。但是我无法标记您的答案,因为srgfed01首先回答了有关MonadicJValue的问题,因此希望您能理解:)。顺便说一句,我也想告诉你,当我尝试时,关于why I cant do this? for ( rec <- json \"records"...的示例代码不起作用。此行存在错误:rec = obj.rec。错误消息说:value rec is not a member of org.json4s.JValue。
-
该代码示例已经修复..我的疏忽。
-
运行它时出现错误:error: type mismatch; found : org.json4s.JsonAST.JObject => (org.json4s.JsonAST.JObject, List[org.json4s.JsonAST.JField]); (which expands to) org.json4s.JsonAST.JObject => (org.json4s.JsonAST.JObject, List[(String, org.json4s.JsonAST.JValue)]); required: org.json4s.JValue => ?; (which expands to) org.json4s.JsonAST.JValue => ?; obj: JObject <- json \"records"
-
有史以来最好的书面问答之一
-
@CJLam-谢谢:)
只是简化的示例,for-comprehesion在这里如何工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| scala > trait A
defined trait A
scala > case class A2 (value : Int ) extends A
defined class A2
scala > case class A3 (value : Int ) extends A
defined class A3
scala > val a = List (1, 2, 3)
a : List [Int ] = List (1, 2, 3)
scala > val a : List [A ] = List (A2 (1),A3 (2),A2 (3))
a : List [A ] = List (A2 (1), A3 (2), A2 (3)) |
所以这只是:
1 2
| scala > for(A2 (rec ) <- a ) yield rec //will return and unapply only A2 instances
res34 : List [Int ] = List (1, 3) |
等效于:
1 2
| scala > a. collect{case A2 (rec ) => rec }
res35 : List [Int ] = List (1, 3) |
Collect基于filter-因此具有与JValue相同的filter方法就足够了。
附言JValue中没有foreach-因此这在for(rec <- json \"records") rec中不起作用。但是有map,因此将:for(rec <- json \"records") yield rec
如果您需要不带模式匹配的for:
1 2 3 4 5 6 7 8 9 10
| for {
rec <- (json "records"). filter(_. isInstanceOf[JObject ]). map(_. asInstanceOf[JObject ])
rcobj = rec. obj
name <- rcobj if name. _1 =="name"
address <- rcobj if address. _1 =="address"
nm = name. _2. asInstanceOf[JString ]. s
vl = address. _2. asInstanceOf[JString ]. s
} yield Map ("name" - > nm, "address" - > vl )
res27 : List [scala. collection. immutable. Map[String,String ]] = List (Map (name - > John Derp, address - > Jem Street 21), Map (name - > Scala Jo, address - > in my sweet dream )) |
-
谢谢,您的示例非常简单明了:)。但是关于JValue中的foreach,当我尝试for(rec <- json \"records") yield rec时,在屏幕上显示了结果:res29: org.json4s.JValue = JArray(List(JObject(List((id,JString(001)), (desc,JString(test1)))), JObject(List((id,JString(002)),(desc,JString(in test2))))))。也许您对for(rec <- json \"records") println(rec)有误解?
-
哦。我的不好-我在回答中写了yield,但实际上并不意味着它:)
-
好的,没问题:)。现在可以用for { x <- y, ..} yield Map(..)模式创建相同的输出了吗?这意味着在for循环中没有像JObject(rec) <- ..这样的模式匹配。正如rspencer所说,这就是让我感到好奇的唯一一件事。
-
没有任何模式匹配:for(rec <- (json \"records").filter(_.isInstanceOf[JObject]).map(_.asInstanceOf[JObject]); name <- rec.obj.find(_._1 =="name"); addr <- rec.obj.find(_._1 =="address")) yield name -> addr。
-
就是这样,预期输出为:List[Map[String,Any]] = List(Map(name -> John Derp, address -> Jem Street 21), Map(name -> Scala Jo, address -> in my sweet dream))。您返回的仍然是Map [String,JField]的列表。顺便说一句,为什么for comprehension中的行之间必须有分号?
-
for(rec <- (json \"records").filter(_.isInstanceOf[JObject]).map(_.asInstanceOf[JObject]); rcobj = rec.obj; name <- rcobj.find(_._1 =="name"); addr <- rcobj.find(_._1 =="address"); nm = name._2.asInstanceOf[JString].s; vl = addr._2.asInstanceOf[JString].s) yield Map("name" -> nm,"address" -> vl) res27: List[scala.collection.immutable.Map[String,String]] = List(Map(name -> John Derp, address -> Jem Street 21), Map(name -> Scala Jo, address -> in my sweet dream)) Ive使用分号只是将其写为单行
-
太好了,您做到了:)。您能否将代码放入答案中,以便任何人都能更好地阅读?奇怪的是,如果我不输入分号,则会出现编译器错误:) expected but <- found.。你知道为什么吗?可能是您在for中的代码不理解吗?
-
知道了您正在使用"("和")",所以这就是为什么它需要分号的原因。如果我使用" {"和"}",则不需要它。
-
更新。只是为了完成有关类别理论的某些知识(就像Sheldon的"带标志的乐趣"播客一样)-在MonadicJValue类型类中没有foreach,因为foreach带有副作用,但在foreach中却没有副作用。纯FP。但这实际上不是Monadic,因为JsValues之间没有flatMap方法或至少有一些concat(因为它可以为您提供flatten与fold的组合)
-
非常感谢 :)。这行上是否可能:可以通过使用if ...将rec <- (json \"records").filter(_.isInstanceOf[JObject]).map(_.asInstanceOf[JObject])缩短,可以吗?关于Monadic,与Monad含义相同吗? Scala中的Monad是否是具有map,flatMap和filter操作的对象?
-
同样,flatMap对于monad就足够了。如果您只有map,则它只是一个函子。
-
等待,在这行(name, nm) <- rcobj中,输入(name, nm)元组,这有点像模式匹配吗?在这种情况下,是否为JField(x,y)匹配项?另外,对于if name =="name",您此时不使用find方法包装它,是因为find和filter相同吗? (我听说if ..在理解中被翻译为filter)
-
如果只有一个匹配的元素-查找和过滤器在逻辑上是相同的。删除元组
-
那么元组与您之前提到的类别理论有关吗?您能否解释一下类别理论?
-
元组-类别理论中的此类产品。您也可以检查有关类比的问题/答案stackoverflow.com/questions/27752424/
-
在阅读Learning Scalaz之前,我是否应该先阅读一本基础的Scala书(可能像Martin oderskys的书)?后面带有z字母(例如《七龙珠Z》)对我来说听起来很顽固:)
-
你应该 :)。但是您也可以将scalaz与维基百科结合起来并随机阅读。
-
Scalaz-实际上是Haskells库采用的Scala
-
范畴论有点像捷径吗?所以这里有X(JObject)-> Y(JField)-> Z(JString)。快捷方式路径是X-> Z,产生X->(Y,Z)。
-
类别理论就像理论一样-因此您应该花很多时间来了解它是什么:)。无论如何,所有内容都以最抽象的含义涉及对象和箭头,因此没有关于快捷方式的规范。如果您指的是可交换图-它们的唯一限制是可交换-图中不同路径(具有任意长度)的组合应给出相同的结果。换句话说,从A到B的任何可能方式(态射组成)都应为您提供B。