How to merge a JsValue to JsObject in flat level

前端 未结 2 753
囚心锁ツ
囚心锁ツ 2021-02-05 13:59

I have two JsValue created from case class, i.e. Book and Book detail

val bookJson = Json.tojson(Book)
val bookDetailJson = Json.tojson(BookDetail)
2条回答
  •  借酒劲吻你
    2021-02-05 14:14

    Play has a lot of new features for JSON right now. This would be a nice showcase for the Format[A] trait (see Scala Json Inception) which you could include implicitly as I will show, or explicitly to the methods that require an implicit Format[A]/Reads[A]/Writes[A].

    Create a case class to represent your JSON objects,

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

    Create companion objects that contain the implicit Format[A] so that Format/Reads/Writes will automatically be in scope when you need them.

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

    Now you could do something like this,

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

    And you will have an object like this,

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

    I've tried this in the REPL but it does not work, in a Play application it does just fine though. Also in a production scenario we would likely use asOpt[T] in place of as[T].

    Here is an example of why asOpt[T] may be better suited, suppose instead of a valid JSON object for book you get,

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

    You will end up with a

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

    But suppose instead you change your method to use asOpt[T],

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

    Now you will end up with at least a partial JSON object,

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

    So depending on how you would like to handle improperly formatted JSON you could choose either option.

提交回复
热议问题