如何摆脱这种隐式转换?
How to get rid of this implicit conversion?
假设我正在使用 json4s
来解析 JSON:
val str = """{"a":"aaaa", "x": 0}"""
val json = JsonMethods.parse(str)
val a = for(JObject(fields) <- json; JField("a", JString(a)) <- fields) yield a
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
隐式转换有效,但我不喜欢它。你有什么建议?
一种方法是使用 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. There are also more examples。
然后json.read[A]
returns一个Error \/ A
and json.validate[A]
yields a Validation
types from scalaz. There are similar types in 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
假设我正在使用 json4s
来解析 JSON:
val str = """{"a":"aaaa", "x": 0}"""
val json = JsonMethods.parse(str)
val a = for(JObject(fields) <- json; JField("a", JString(a)) <- fields) yield a
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
隐式转换有效,但我不喜欢它。你有什么建议?
一种方法是使用 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. There are also more examples。
然后json.read[A]
returns一个Error \/ A
and json.validate[A]
yields a Validation
types from scalaz. There are similar types in 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