Skip to content

Commit

Permalink
Add PlayJson module.
Browse files Browse the repository at this point in the history
  • Loading branch information
quincyjo committed Dec 29, 2023
1 parent dfeff9d commit 40c09bc
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 2 deletions.
16 changes: 14 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ val catsCore = "org.typelevel" %% "cats-core" % catsVersion
val circeVersion = "0.14.6"
val circeCore = "io.circe" %% "circe-core" % circeVersion

val playJsonVersion = "3.0.1"
val playJson = "org.playframework" %% "play-json" % playJsonVersion

// skip / publish := true
ThisBuild / version := "0.1"
// Default to same as circe or SBT isn't happy.
Expand Down Expand Up @@ -62,8 +65,7 @@ lazy val core = project
.settings(
name := "Scala Jsonpath",
moduleName := "scala-json-path",
commonSettings,
crossScalaVersions := List(Scala2_13, Scala3)
commonSettings
)

lazy val circe = project
Expand All @@ -85,3 +87,13 @@ lazy val circe = project
)
)
)

lazy val play = project
.in(file("modules/play"))
.dependsOn(core)
.settings(
name := "Scala Jsonpath Play",
moduleName := "scala-json-path-play",
commonSettings,
libraryDependencies += playJson
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.quincyjo.jsonpath.play

import com.quincyjo.jsonpath.JsonPathEvaluator
import com.quincyjo.jsonpath.play.implicits.playSupport
import play.api.libs.json.JsValue

object PlayEvaluator extends JsonPathEvaluator[JsValue]
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.quincyjo.jsonpath.play

import com.quincyjo.jsonpath.JsonSupport
import play.api.libs.json._

trait PlaySupport {

implicit val playSupport: JsonSupport[JsValue] = PlaySupport
}

object PlaySupport extends JsonSupport[JsValue] {

override def string(string: String): JsValue =
JsString(string)

override def number(bigDecimal: BigDecimal): JsValue =
JsNumber(bigDecimal)

override def boolean(boolean: Boolean): JsValue =
JsBoolean(boolean)

override def Null: JsValue =
JsNull

override def asObject(json: JsValue): Option[Map[String, JsValue]] =
json match {
case JsObject(underlying) => Option(underlying.toMap)
case _ => None
}

override def asArray(json: JsValue): Option[Vector[JsValue]] =
json match {
case JsArray(values) => Some(values.toVector)
case _ => None
}

override def asString(json: JsValue): Option[String] =
json match {
case JsString(value) => Some(value)
case _ => None
}

override def asBoolean(json: JsValue): Option[Boolean] =
json match {
case boolean: JsBoolean => Some(boolean.value)
case _ => None
}

override def asNumber(json: JsValue): Option[BigDecimal] =
json match {
case JsNumber(value) => Some(value)
case _ => None
}

override def asNull(json: JsValue): Option[Unit] =
json match {
case JsNull => Some(())
case _ => None
}

override def isObject(json: JsValue): Boolean =
json match {
case JsObject(_) => true
case _ => false
}

override def isArray(json: JsValue): Boolean =
json match {
case JsArray(_) => true
case _ => false
}

override def isString(json: JsValue): Boolean =
json match {
case JsString(_) => true
case _ => false
}

override def isBoolean(json: JsValue): Boolean =
json match {
case JsBoolean(_) => true
case _ => false
}

override def isNumber(json: JsValue): Boolean =
json match {
case JsNumber(_) => true
case _ => false
}

override def isNull(json: JsValue): Boolean =
json match {
case JsNull => true
case _ => false
}

override def fold[B](json: JsValue)(
ifNull: => B,
jsonBoolean: Boolean => B,
jsonNumber: BigDecimal => B,
jsonString: String => B,
jsonArray: Vector[JsValue] => B,
jsonObject: Map[String, JsValue] => B
): B = json match {
case JsNull => ifNull
case boolean: JsBoolean => jsonBoolean(boolean.value)
case JsNumber(value) => jsonNumber(value)
case JsString(value) => jsonString(value)
case JsArray(value) => jsonArray(value.toVector)
case JsObject(underlying) => jsonObject(underlying.toMap)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.quincyjo.jsonpath.play

import com.quincyjo.jsonpath.JsonSupport
import play.api.libs.json.JsValue

package object implicits {

implicit val playSupport: JsonSupport[JsValue] = PlaySupport
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.quincyjo.jsonpath.play

import com.quincyjo.jsonpath.Expression._
import com.quincyjo.jsonpath.JsonPath._
import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.should.Matchers
import play.api.libs.json._

class PlayEvaluatorSpec extends AnyFlatSpecLike with Matchers {

private val apple = Json.obj(
"label" -> "Apple",
"price" -> 2,
"quantity" -> 15
)
private val banana = Json.obj(
"label" -> "Banana",
"price" -> 1,
"quantity" -> 23
)
private val json = Json.obj(
"products" -> Json.arr(
apple,
banana,
Json.obj(
"label" -> "Dinner Set",
"price" -> 30,
"quantity" -> 2
)
)
)

"evaluate" should "behave" in {
val jsonPath = $ / "products" / Filter(
LessThanOrEqualTo(
JsonPathValue(`@` / "price"),
JsonNumber(10)
)
)

PlayEvaluator.evaluate(
jsonPath,
json
) should contain theSameElementsAs Seq(
apple,
banana
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2023 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.quincyjo.jsonpath.play

import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import play.api.libs.json._

class PlaySupportSpec
extends AnyFlatSpecLike
with Matchers
with TableDrivenPropertyChecks {

"asObject" should "only be defined for objects" in {
val cases = Table[JsValue, Option[Map[String, JsValue]]](
("given", "expected"),
(JsNull, None),
(JsString("foo"), None),
(JsBoolean(true), None),
(JsNumber(42), None),
(Json.arr(true), None),
(Json.obj(), Some(Map.empty)),
(
Json.obj("foo" -> "bar"),
Some(Map("foo" -> JsString("bar")))
)
)

forAll(cases) { case (json, expected) =>
PlaySupport.asObject(json) should be(expected)
}
}
}

0 comments on commit 40c09bc

Please sign in to comment.