假设我使用val str = """{"a":"aaaa", "x": 0}"""
val json = JsonMethods.parse(str)
val a = for(JObject(fields) <- json; JField("a", JString(a)) <- fields) yield a
来解析JSON:
a
List[String]
的类型为Option[String]
,但我需要headOption
,所以我打电话给val a = (
for(JObject(fields) <- json; JField("a", JString(a)) <- fields) yield a
).headOption
:
headOption
由于我发现自己一次又一次地调用object X { implicit def foo[A](as: List[A]): Option[A] = as.headOption }
import X.foo
val a: Option[String] =
for(JObject(fields) <- json; JField("a", JString(a)) <- fields) yield a
,我尝试了隐式转换:
MASS::write.matrix
隐式转换正在运行,但我不喜欢它。你会建议什么?
答案 0 :(得分:3)
一种方法是使用json4的类型类,例如json4s-scalaz有以下内容:
trait JSONR[A] {
def read(json: JValue): Result[A]
}
trait JSONW[A] {
def write(value: A): JValue
}
为了简化语法,可以为JValue
定义扩展方法:
implicit class JValueOps(value: JValue) {
def validate[A: JSONR]: ValidationNel[Error, A] = implicitly[JSONR[A]].read(value)
def read[A: JSONR]: Error \/ A = implicitly[JSONR[A]].read(value).disjunction.leftMap(_.head)
}
然后进行遍历并解析结果JValue
这样的遍历:
val str =
"""
|{
| "a": "aaaa",
| "x": 0
|}""".stripMargin
val json = parseJson(str)
(json \ "a").read[Option[String]]
// \/-(Some(aaaa))
(json \ "b").read[Option[String]]
// \/-(None)
(json \ "a").validate[Option[String]]
// Success(Some(aaaa))
(json \ "b").validate[Option[String]]
// Success(None)
可以这样定义您自己的JSONR[A]
/ JSONW[A]
个实例(并将它们置于隐式范围内):
case class MyA(a: Option[String], x: Int)
implicit val myARead: JSONR[MyA] = JSON.readE[MyA] { json =>
for {
a <- (json \ "a").read[Option[String]]
x <- (json \ "x").read[Int]
} yield MyA(a, x)
}
implicit val myAWrite: JSONW[MyA] = JSON.write[MyA] { myA =>
("a" -> myA.a) ~
("x" -> myA.x)
}
json.read[MyA]
// \/-(MyA(Some(aaaa),0))
json.validate[MyA]
// Success(MyA(Some(aaaa),0))
MyA(Some("aaaa"), 0).toJson
// JObject(List((a,JString(aaaa)), (x,JInt(0))))
请注意,read[A]
和write[a]
方法是粘合代码,而json4s-scalaz尚未提供,您可以找到源here。还有更多examples。
然后json.read[A]
返回Error \/ A
,json.validate[A]
会从Validation
产生scalaz种类型。 cats中有类似的类型。
myARead
是monadic解析样式的一个例子(通过flatMap构图)。另一种方法是使用适用的解析。这样做的好处是累积了所有验证错误:
val myARead2: JSONR[MyA] = JSON.read[MyA] { json =>
(
(json \ "a").validate[Option[String]] |@|
(json \ "x").validate[Int]
).tupled.map(MyA.tupled)
}
val myARead3: JSONR[MyA] = JSON.read[MyA] {
for {
a <- field[Option[String]]("a") _
x <- field[Int]("x") _
} yield (a |@| x).tupled.map(MyA.tupled)
}
还有https://github.com/json4s/json4s/blob/3.4/core/src/main/scala/org/json4s/JsonFormat.scala