通过读取[t]将JSVALUE转换为模型,该模型由元组列表组成



我有以下类:

case class Model(elements: List[(String, String)])

现在,我想通过使用Reads[T]填充我的模型Model。JSON可以具有不同的键值对,在解释它们时未知,因此我想将它们作为元组列表。

例如:

{ "foo": "bar", "barfoo": "foobar"}

应该成为:

List(("foo" -> "bar"), ("barfoo" -> "foobar"))

问题是我不知道如何实现与JSON对象中所有元素但不嵌套或数组中所有元素相匹配的通配符函数。

implicit val modelReads: Reads[Model] = (
        (JsPath  "?").read[String] // and
     // (JsPath  "foo").read[String] // and <- key not known in advance
     // (JsPath  "barfoo").read[String] // <- key not known in advance
        ) (Model.apply _)

您将无法在此处使用播放JSON组合器,因为它们仅与固定的字段映射一起使用。为了使您能够读取elements字段,您需要实现Reads[List[(String, String)]]。幸运的是,Play已经有一个Reads[Map[A, B]]可用(对于也具有Reads的类型AB),并且可以轻松地将Map[A, B]转换为List[(A, B)](在Map下方是CC_12只是一个元组的集合)。

>

对于一次性情况,我们可以将read[Map[String, String]]map使用到List。然后,我们可以将其映射到案例类。假设以下JSON结构:

val js = Json.parse("""{"element": { "foo": "bar", "barfoo": "foobar"}}""")

您可以写:

implicit val reads = (__  "elements").read[Map[String, String]]
  .map(_.toList)
  .map(tuples => Model(tuples))

然后尝试:

scala> js.validate[Model]
res8: play.api.libs.json.JsResult[Model] = JsSuccess(Model(List((foo,bar), (barfoo,foobar))),/elements)

请注意,上面的Reads[Model]是一种特殊情况,因为案例类只有一个字段。要将其进一步了解一下,看看它如何与JSON组合器一起玩,让我们添加一个新字段:

case class Model(elements: List[(String, String)], info: String)

然后,让我们对元组进行更通用的Reads,以便可以处理可用Reads[A]的任何类型的A的值:

implicit def tupleReads[A](implicit rds: Reads[A]): Reads[List[(String, A)]] =
  Reads.mapReads(rds).map(_.toList)

现在,我们可以使用组合符为新定义的JsValue1编写Reads,与您使用的相同:

implicit val reads = (
  (__  "elements").read[List[(String, String)]] and
  (__  "info").read[String]
)(Model.apply _)

尝试一下:

val js = Json.parse("""{"elements": { "foo": "bar", "barfoo": "foobar"}, "info": "test"}""")
scala> js.validate[Model]
res0: play.api.libs.json.JsResult[Model] = JsSuccess(Model(List((foo,bar), (barfoo,foobar)),test),)

如果您的JSON结构看起来像{"foo": "bar", "barfoo": "foobar"}(没有elements键),那么我们仍然可以利用相同的通用Reads[List[(String, A)]],但是需要实现更自定义的Reads[Model]来将整个对象映射到一个模型字段。让我们要将以上JSON映射到:

Model(List(("foo" -> "bar"), ("barfoo" -> "foobar")))

我们需要的Reads[Model]基本上将与我定义的第一个相同,除了我们可以从中删除JsPath

// Use `tupleReads` as defined above, restricted to `String`
implicit val reads = tupleReads[String].map(tuples => Model(tuples))

它有效:

val js = Json.parse("""{"foo": "bar", "barfoo": "foobar"}""")
scala> js.validate[Model]
res0: play.api.libs.json.JsResult[Model] = JsSuccess(Model(List((foo,bar), (barfoo,foobar))),)

这是草稿代码:

val json = Json.parse("""
  { "foo": "bar", "barfoo": "foobar"}
""")
implicit val readMetaTag = 
  Reads(js => JsSuccess(
      Model(js.as[JsObject].fieldSet.map(
          tag => (tag._1, tag._2.as[String])).toList)))
val model = json.as[Model]
println("Model: " + model)
//Model: Model(List((foo,bar), (barfoo,foobar)))

最新更新