3

I'm using Scala Play 2.7.2 and have read ScalaJsonTransformers and ScalaJson. After calling a JSON API I get back (simplified MCVE) results like this:

{
  "type": "searchset",
  "total": 5,
  "entry": [
    {
        "start": "2019-06-07T09:00:00",
        "end": "2019-06-07T11:00:00",
        "id": "55"
    },
    {
        "start": "2019-06-07T13:00:00",
        "end": "2019-06-07T15:00:00",
        "id": "56"
    },
    {
        "start": "2019-06-07T16:00:00",
        "end": "2019-06-07T17:00:00",
        "id": "60"
    },
    {
        "start": "2019-06-10T11:00:00",
        "end": "2019-06-10T12:00:00",
        "id": "58"
    },
    {
        "start": "2019-06-11T14:00:00",
        "end": "2019-06-11T15:00:00",
        "id": "61"
    }
  ]
}

and I'd like to filter the results and choose only the ones that satisfy a condition, for example, filter out the ones whose end date is greater than certain date val to = new DateTime("2019-06-10T00:00:00") and doing something alla:

(json \\ "end").filter(new DateTime(_).isBefore(to.toDate.getTime))...

but this doesn't work because the result is the selection and not the entire json node and besides it leaves the outer part too.

The solution should output the result:

{
  "type": "searchset",
  "total": 3,
  "entry": [
    {
        "start": "2019-06-07T09:00:00",
        "end": "2019-06-07T11:00:00",
        "id": "55"
    },
    {
        "start": "2019-06-07T13:00:00",
        "end": "2019-06-07T15:00:00",
        "id": "56"
    },
    {
        "start": "2019-06-07T16:00:00",
        "end": "2019-06-07T17:00:00",
        "id": "60"
    }
}

How can this be done using Play JSON?

Mario Galic
  • 41,266
  • 6
  • 39
  • 76
SkyWalker
  • 11,704
  • 14
  • 65
  • 144

1 Answers1

3

For coast-to-coast design try defining update transformer like so

val to = new DateTime("2019-06-10T00:00:00")

val endDateFilterTransformer = (__ \ 'entry).json.update(__.read[JsArray].map {
  case JsArray(values) => JsArray(values.filter(v => (v \ "end").as[DateTime].isBefore(to)))
})

val outJson = json.transform(endDateFilterTransformer)
println(outJson.get)

which outputs

{
  "entry": [
    {
      "start": "2019-06-07T09:00:00",
      "end": "2019-06-07T11:00:00",
      "id": "55"
    },
    {
      "start": "2019-06-07T13:00:00",
      "end": "2019-06-07T15:00:00",
      "id": "56"
    },
    {
      "start": "2019-06-07T16:00:00",
      "end": "2019-06-07T17:00:00",
      "id": "60"
    }
  ],
  "total": 5,
  "type": "searchset"
}

Alternatively for JSON to OO design try deserialising to a model

case class Entry(start: DateTime, end: DateTime, id: String)
object Entry {
  implicit val format = Json.format[Entry]
}
case class Record(`type`: String, total: Int, entry: List[Entry])

object Record {
  implicit val format = Json.format[Record]
}

then filter using regular Scala methods

val to = new DateTime("2019-06-10T00:00:00")
val record = Json.parse(raw).as[Record]
val filteredRecord = record.copy(entry = record.entry.filter(_.end.isBefore(to)))

then deserialise back to json like so:

Json.toJson(filteredRecord)

which outputs

{
  "type": "searchset",
  "total": 5,
  "entry": [
    {
      "start": "2019-06-07T09:00:00.000+01:00",
      "end": "2019-06-07T11:00:00.000+01:00",
      "id": "55"
    },
    {
      "start": "2019-06-07T13:00:00.000+01:00",
      "end": "2019-06-07T15:00:00.000+01:00",
      "id": "56"
    },
    {
      "start": "2019-06-07T16:00:00.000+01:00",
      "end": "2019-06-07T17:00:00.000+01:00",
      "id": "60"
    }
  ]
}

where we use play-json-joda for DateTime serialisation

libraryDependencies += "com.typesafe.play" %% "play-json-joda" % "2.7.3"
import play.api.libs.json.JodaWrites._
import play.api.libs.json.JodaReads._
Mario Galic
  • 41,266
  • 6
  • 39
  • 76
  • Hi @Mario Galic thank you again! It's deliberate not to use serialization here. I'm building an App JSON API on top of an external JSON API provider to feed my Angular frontend. Therefore my Play side is a pass-through and don't want to be serializing it if you know what I mean. – SkyWalker Jun 12 '19 at 08:34
  • Upvoted as this answer could be of use to others who can consider serialization. – slugmandrew Jun 12 '19 at 09:32
  • Wow cool I knew the coast-to-coast design could cut ... just didn't manage to work out how :) – SkyWalker Jun 12 '19 at 10:08