web-dev-qa-db-ja.com

JsValueをフラットレベルでJsObjectにマージする方法

ケースクラスから作成された2つのJsValue、つまり本と本の詳細があります

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にマージするにはどうすればよいですか?つまり.

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

変換を試みていましたが、2番目のJsValueを反復処理できませんでした。

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

bookJson.validate(mapDetail).get

それは1レベル下になるでしょう、それは私が本当に望んでいないことです。

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

このJson変換で何かトリックが提供できるかどうか教えてください。どうもありがとう!

13
Joyfulvillage

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をどのように処理するかに応じて、いずれかのオプションを選択できます。

18
tysonjh

JsObjectはJsValueのサブタイプです。

JsValueは、asまたはasOptメソッドを使用してJsObjectに簡単に変換できます。 JsValue。例:

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

JsArrayの場合では、上記のコードは使用できません。 playを使用して配列でJSONを解析すると、Json.toJson(...)は実際にはJsArrayであるJsValueを生成します。 JsArrayを次のように変換する必要があります。

val someJsValueButArray = ....
val asJsArray:JsArray = Json.toJson(someJsValueButArray).as[JsArray]
val asSeqOfJsObjects:Seq[JsObject] = asJsArray.value.map(_.as[JsObject])
2
albgorski