通过由元组列表组成的Reads [T]将JsValue转换为模型 [英] Convert a JsValue to a model via Reads[T] which consists of a list of tuples

查看:154
本文介绍了通过由元组列表组成的Reads [T]将JsValue转换为模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下课程:

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

现在,我想通过使用Reads[T]JsValue的值填充模型Model. JSON可能具有不同的键值对,在解组它们时这些键值对是未知的,因此我希望将它们作为元组列表使用.

Now I want to fill my model Model with the values of a JsValue by using Reads[T]. The JSON could have different key values pairs which are unknown at the time of unmarshaling them and therefore I want to have them as a list of tuples.

例如:

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

应成为:

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

问题是我不知道如何实现一种与JSON对象中的所有元素匹配的通配符函数,而不与嵌套的元素或数组匹配.

The problem is that I don't know how I can achieve a sort of wildcard function that matches all elements in a JSON object, but not nested ones or arrays.

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 _)

推荐答案

在这里,您将无法对所有内容使用Play JSON组合器,因为它们仅适用于固定字段映射.为了能够读取elements字段,您需要实现Reads[List[(String, String)]].幸运的是,Play已经提供了Reads[Map[A, B]](对于同时具有Reads的类型AB),并且Map[A, B]可以轻松转换为List[(A, B)](在Map下面是只是一个元组的集合.

You won't be able to use Play JSON combinators for everything here, as they only work with fixed field mappings. For you to be able to read the elements field, you would need to implement a Reads[List[(String, String)]]. Fortunately, Play already has a Reads[Map[A, B]] available (for types A and B that also have a Reads), and a Map[A, B] can easily be converted into a List[(A, B)] (underneath a Map is just a collection of tuples).

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

For a one-off case, we can use read[Map[String, String]] and map it to a List. Then, we can map that to the case class. Assuming the following JSON structure:

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]是一种特殊情况,因为case类只有一个字段.为了更进一步了解它如何与JSON组合器一起使用,让我们添加一个新字段:

Note that the Reads[Model] above is kind of a special case, because the case class only had a single field. To take this a bit further and see how it can play with JSON combinators, let's add a new field:

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

然后,我们还使元组的Reads更加通用,以便它可以处理A可用的任何A类型的值:

Then, let's also make our Reads for the tuples a little more generic, so that it can handle values of any type A where a Reads[A] is available:

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

现在,我们可以使用组合器为新定义的Model编写Reads,就像您以前习惯的那样:

Now we can write a Reads using combinators for the newly defined Model, the same as you're used to:

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映射到:

If your JSON structure only looks like {"foo": "bar", "barfoo": "foobar"} (without an elements key), then we can still leverage the same generic Reads[List[(String, A)]], but will need to implement a more custom Reads[Model] to map an entire object to one model field. Let's we want to map the above JSON to:

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

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

The Reads[Model] we need will basically be the same as the first one I defined, except that we can drop the JsPath from it:

// 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))),)

这篇关于通过由元组列表组成的Reads [T]将JsValue转换为模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆