14

我有两个从案例类创建的 JsValue,即 Book 和 Book detail

val bookJson = Json.tojson(Book)
val bookDetailJson = Json.tojson(BookDetail)

格式为:

//Book
{
  id: 1,
  name: "A Brief History of Time"
}

//BookDetail
{
  bookId: 1,
  author: "Steven Hawking",
  publicationDate: 1988,
  pages: 256
}

如何将它们合并到 play-framework 2.10 中的单个 Json?IE

//Book with detail
{
  id: 1,
  name: "A Brief History of Time",
  bookId: 1,
  author: "Steven Hawking",
  publicationDate: 1988,
  pages: 256
}

我正在尝试转换,但未能遍历第二个 JsValue:

val mapDetail = (__).json.update(
                  __.read[JsObject].map { o =>
                  o.deepMerge( JsObject(Seq(("detail", bookDetailJson))) )
                })

bookJson.validate(mapDetail).get

它会降低一层,我真的不想要。

//Book with detail
{
  id: 1,
  name: "A Brief History of Time",
  detail: {
            bookId: 1,
            author: "Steven Hawking",
            publicationDate: 1988,
            pages: 256
          }
}

请让我知道是否有任何技巧可以提供这个 Json 转换。非常感谢!

4

2 回答 2

18

Play 现在为 JSON 提供了很多新功能。这将是一个很好的展示Format[A]特性(参见Scala Json Inception),您可以隐含地包含它,我将展示它,或者显式地包含在需要隐含的方法中Format[A]/Reads[A]/Writes[A]

创建一个案例类来表示您的 JSON 对象,

case class Book(id: Int, name: String)
case class BookDetail(id: Int, author: String, publicationDate: Int, pages: Int)

创建包含隐式的伴随对象,Format[A]以便Format/Reads/Writes在您需要它们时自动在范围内。

object Book { 
  implicit val fmt: Format[Book] = Json.format[Book] 
}

object BookDetail { 
  implicit val fmt: Format[BookDetail] = Json.format[BookDetail] 
}

现在你可以做这样的事情,

val bookJson = Json.toJson(Book(1, "A Brief History Of Time"))
val bookDetailJson = Json.toJson(BookDetail(1, "Steven Hawking", 1988, 256))
bookJson.as[JsObject].deepMerge(bookDetailJson.as[JsObject])

你会有一个像这样的对象,

{
  id: 1,
  name: "A Brief History Of Time",
  author: "Steven Hawking",
  publicationDate: 1988,
  pages: 256
}

我已经在 REPL 中尝试过,但它不起作用,但在 Play 应用程序中它做得很好。同样在生产场景中,我们可能会使用asOpt[T].as[T]

这是一个为什么asOpt[T]可能更适合的示例,假设您获得的书不是有效的 JSON 对象,

val bookJson = Json.toJson("not a book")

你最终会得到一个

[JsResultException: JsResultException(errors:List((,List(ValidationError(validate.error.expected.jsobject,WrappedArray())))))]

但是假设您更改了使用方法asOpt[T]

bookJson.asOpt[JsObject].getOrElse(Json.obj()).deepMerge(bookDetailJson.asOpt[JsObject].getOrElse(Json.obj()))

现在你至少会得到一个部分 JSON 对象,

{
  id: 1,
  author: "Steven Hawking",
  publicationDate: 1988,
  pages: 256
}

因此,根据您希望如何处理格式不正确的 JSON,您可以选择任一选项。

于 2013-10-23T20:03:07.460 回答
2

JsObject 是 JsValue 的子类型。

JsValue 可以使用 JsValue 中的asasOpt方法简单地转换为 JsObject。例子:

val someJsValue = ....
val asObject:JsObject = someJsValue.as[JsObject]
val asObjectMaybe:Option[JsObject] = v.asOpt[JsObject]

JsArray 的情况下,您不能使用上面的代码。如果你使用 play 并用数组解析 JSON,那么Json.toJson(...)会产生 JsValue ,它实际上是 JsArray 。您需要将 JsArray 转换如下:

val someJsValueButArray = ....
val asJsArray:JsArray = Json.toJson(someJsValueButArray).as[JsArray]
val asSeqOfJsObjects:Seq[JsObject] = asJsArray.value.map(_.as[JsObject])
于 2016-04-22T14:05:21.993 回答