简体   繁体   中英

Playframework 2.0.X scala - How to implement multiple json reads/writes for an object?

Let's assume that have a json object Test. It has two formats :

{
    "name": "Test",
    "id": "41"
}

and

{
    "object": {
        "name": "Test",
        "id": "41"
    }
}

I created Test.scala :

case class Test (name: String, id: String)

object Test {

    implicit object FopReads extends Format[Test] {

        def reads(json: JsValue): Test = {
            Test (
                (json \ "name").as[String],
                (json \ "id").as[String]
            )
        }

        def writes(ts: Test) = JsObject(Seq(
            "name" -> JsString(ts.name),
            "id" -> JsString(ts.id)
        ))

    }
}

As you can see, actually, I only deal with the first format. My question is : How do I easily implement a Format[Test] that it can read and write in both formats ? I know that the json mechanism have changed in play 2.10. Should I change for this version to do what I want ?

Thank you for your time !

In my humble opinion, your approach is not ideal and you have to rethink about your design. Let me explain you why.

While you could effectively develop a Format[A] which is able to read both formats, you are not able to write a Format[A] which can write the two formats

trait Format[T]{
    def reads (json: JsValue): T
    def writes (o: T): JsValue
}

As you see, the writes method has a single parameter, so your Format[Test] won't be able to know if it should use one or the other output format. If you followed the reasoning you would agree that the Format trait is delegated to write and read from/to a specific Json Format, not to choose between formats. Your logic to choose the right format has nothing to do with reading and writing the right format, and you should decouple it.

Saying that, you can re-use your Format[Test] in the following way:

case class ObjectParser(nestedFormatter:Format[T]) extends Format[T]{
    def reads (json: JsValue): T = nestedFormatter.read(json \ "object")
    def writes (o: T): JsValue  = JsObject("object" -> nestedFormatter.write(o))
}

This class would take any existing formatter and make it able to read and write inside an additional "object" node.

Imagine now you have a controller which needs to generate or parse two different answers according to the url endpoints:

object TestController extends Controller {

    def endPoint1Action = Action{
        implicit request => 
        generateResponse(FopReads)
    }

    def endPoin21Action = Action{
        implicit request => 
        generateResponse(ObjectParser(FopReads))
    }

    def generateResponse(formatter:Format[Test])(implicit request:Request[AnyContent]):Response  = BadRequest("!")

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM