json4s:跨域提取

json4s: cross field extraction

json4s extract 基于 DefaultFormatsCustomSerializer.

中定义的每种类型规则的 JsonAST

有时候想跨场提取。例如,给定一个 json 字符串 {"a": 1, "b": 2},我想将 b 的值设置为 a+b。我能做到:

import org.json4s._
import org.json4s.native.JsonMethods._
import org.json4s.JsonAST._

case class A(a: Int, b: Int)

case object ACustomSerializer extends CustomSerializer[A](
    format =>
    ({
        case jo: JObject =>
            val a = (jo \ "a").extract[Int]
            val b = (jo \ "b").extract[Int] + a
            A(a, b)
    }, Map())
)

implicit val formats = DefaultFormats + ACustomSerializer

parse("""{"a": 1, "b": 2}""").extract[A] // A(1,3)

但是,如果 case class A 有很多其他字段,则很难为所有字段编写规则。

case class A(a: Int, b: Int, c: Int, d: Int)

case object ACustomSerializer extends CustomSerializer[A](
    format =>
    ({
        case jo: JObject =>
            val a = (jo \ "a").extract[Int]
            val b = (jo \ "b").extract[Int] + a
            val c = ...
            val d = ...
            A(a, b, c, d)
    }, Map())
)

如果我们不希望字段 b 有 "cross field extraction",它们本可以由 DefaultFormats 或其他 CustomSerializer 处理。如果案例 class 实际上很大,情况会变得更糟。

有没有办法只为特殊字段编写规则,而让 DefaultFormatsCustomSerialzer 处理其余部分?

一般来说,最好按原样解析数据,然后再进行处理(为了维护separation of concerns)。

在这种情况下,它看起来像这样:

val a = parse("""{"a": 1, "b": 2}""").extract[A] // A(1,2)

a.copy(b = a.a + a.b) // A(1,3)

在更复杂的情况下,已处理数据的布局将不同于已解析数据,因此您需要第二个 case class 来描述原始数据和将其转换为已处理格式的函数。虽然这看起来很麻烦,但它会使代码更容易理解并且更易于修改。