使用 circe,很容易在 case 类中解码 Json:
import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._
case class Bar(xs: List[String])
scala> decode[Bar]("{"xs":["a","b"]}")
res2: Either[io.circe.Error,Bar] = Right(Bar(List(a, b)))
scala> decode[Bar]("{"xs":["a","b"],"addedField":true}")
res3: Either[io.circe.Error,Bar] = Right(Bar(List(a, b)))
但是我可以看到一种方法来检查结果中是否已使用和映射所有字段。
目前,检查某些信息是否丢失的一种方法是在 json 中重新转换结果并区分 json(使用 Json4)
scala> val Diff(_,_,removed) = parse(input) diff parse(result.asJson.noSpaces)
removed: org.json4s.JsonAST.JValue = JObject(List((addedField,JBool(true))))
val noLoss = removed == JNothing
有没有办法直接在 circe 中做到这一点?
这里的主要问题是,没有明确的方法来确定解码器是否消耗了字段。
如果我们尝试定义这种消费的一些概念,请按预期工作case class
es:
import shapeless._
import shapeless.labelled.FieldType
trait FieldNames[T] {
def names: Set[String]
}
object FieldNames {
implicit val hNilNames = new FieldNames[HNil] {
def names = Set.empty
}
implicit def hConsNames[S <: Symbol, A, T <: HNil]
(implicit witness: Witness.Aux[S], tail: FieldNames[T]) = new FieldNames[FieldType[S, A] :: T] {
lazy val names = tail.names + witness.value.name
}
implicit def ccNames[CC, Out <: HList]
(implicit lgen: LabelledGeneric.Aux[CC, Out], genNames: Lazy[FieldNames[Out]]) = new FieldNames[CC] {
lazy val names = genNames.value.names
}
}
我们现在可以表示一些剩余的捕手包装器和相应的解码器:
case class Tidy[X](value: X, remains: Option[JsonObject] = None)
object Tidy {
implicit def tidyDecoder[X](implicit decoder: Decoder[X], fields: FieldNames[X]) =
Decoder.instance[Tidy[X]] { cur =>
decoder(cur) map { x =>
Tidy(x, cur.focus.asObject.flatMap { obj =>
Some(obj.filterKeys(!fields.names.contains(_))).filter(_.nonEmpty)
})
}
}
}
有了这个,你可以尝试提取剩磁场:
val json1 =
"""{
"xs" : ["a", "b"]
}
"""
val json2 =
"""{
"xs" : ["a", "b"],
"addedField": true
}
"""
decode[Tidy[Bar]](json1) // Right(Tidy(Bar(List(a, b)),None))
decode[Tidy[Bar]](json2) // Right(Tidy(Bar(List(a, b)),Some(object[addedField -> true])))
没有简单的内置方法。
您可以参考此问题进行 circe 主维护者和本主题中贡献者之间的讨论。
免责声明:没有明确的结论^^