如何摆脱这种隐式转换?

时间:2016-03-08 15:44:53

标签: json scala implicit-conversion json4s

假设我使用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

隐式转换正在运行,但我不喜欢它。你会建议什么?

1 个答案:

答案 0 :(得分:3)

一种方法是使用json4的类型类,例如json4s-scalaz有以下内容:

trait JSONR[A] {
  def read(json: JValue): Result[A]
}

trait JSONW[A] {
  def write(value: A): JValue
}

source

为了简化语法,可以为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 \/ Ajson.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