Skip to content

Commit f4ce9e5

Browse files
author
Charles LADARI
committed
Implement decoder with default decoder
Parse values in jsonLogicData using type field with decoder. Default decoder defined but user can define its own decoder.
1 parent 699deaa commit f4ce9e5

File tree

6 files changed

+177
-12
lines changed

6 files changed

+177
-12
lines changed

src/main/scala/com/github/celadari/jsonlogicscala/core/ComposeLogic.scala

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object ComposeLogic {
66

77
val OPERATORS = Array("<=", "<", ">=", ">", "==", "!=", "or", "and", "xor")
88

9-
private[core] def parse[T](jsonLogic: JsObject, jsonLogicData: JsObject)(implicit fmt: Reads[T]): ComposeLogic[T] = {
9+
private[core] def parse[T](jsonLogic: JsObject, jsonLogicData: JsObject)(implicit fmt: Reads[T]): ComposeLogic = {
1010
// check for operator field
1111
val fields = jsonLogic.fields
1212

@@ -16,10 +16,23 @@ object ComposeLogic {
1616
if (!OPERATORS.contains(operator)) throw new Error(s"Invalid parsed operator: $operator")
1717

1818
// if operator is compose logic
19-
ComposeLogic[T](operator, readArrayOfConditions[T]((jsonLogic \ operator).get, jsonLogicData)(fmt))
19+
ComposeLogic(operator, readArrayOfConditions[T]((jsonLogic \ operator).get, jsonLogicData)(fmt))
2020
}
2121

22-
private[core] def readArrayOfConditions[T](json: JsValue, jsonLogicData: JsObject)(implicit fmt: Reads[T]): Array[JsonLogicCore[_]] = {
22+
private[core] def decode(jsonLogic: JsObject, jsonLogicData: JsObject)(implicit decoder: Decoder): ComposeLogic = {
23+
// check for operator field
24+
val fields = jsonLogic.fields
25+
26+
// check for compose logic operator field
27+
if (fields.length > 1) throw new Error("JSON object is supposed to have only one operator field.")
28+
val operator = fields.head._1
29+
if (!OPERATORS.contains(operator)) throw new Error(s"Invalid parsed operator: $operator")
30+
31+
// if operator is compose logic
32+
ComposeLogic(operator, decodeArrayOfConditions((jsonLogic \ operator).get, jsonLogicData)(decoder))
33+
}
34+
35+
private[core] def readArrayOfConditions[T](json: JsValue, jsonLogicData: JsObject)(implicit fmt: Reads[T]): Array[JsonLogicCore] = {
2336
val jsArray = json.asInstanceOf[JsArray]
2437
jsArray
2538
.value
@@ -29,9 +42,20 @@ object ComposeLogic {
2942
.toArray
3043
}
3144

32-
implicit def composeLogicReads[T](implicit fmt: Reads[T]): Reads[ComposeLogic[T]] = new Reads[ComposeLogic[T]] {
45+
private[core] def decodeArrayOfConditions(json: JsValue, jsonLogicData: JsObject)(implicit decoder: Decoder): Array[JsonLogicCore] = {
46+
val jsArray = json.asInstanceOf[JsArray]
47+
jsArray
48+
.value
49+
.map(jsValue => {
50+
JsonLogicCore.decode(jsValue.asInstanceOf[JsObject], jsonLogicData)(decoder)
51+
})
52+
.toArray
53+
}
54+
55+
56+
implicit def composeLogicReads[T](implicit fmt: Reads[T]): Reads[ComposeLogic] = new Reads[ComposeLogic] {
3357

34-
override def reads(json: JsValue): JsResult[ComposeLogic[T]] = {
58+
override def reads(json: JsValue): JsResult[ComposeLogic] = {
3559
// split json in two components jsonLogic and jsonLogicData
3660
val jsonLogic = (json \ 0).getOrElse(JsObject.empty).asInstanceOf[JsObject]
3761
val jsonLogicData = (json \ 1).getOrElse(JsObject.empty).asInstanceOf[JsObject]
@@ -43,4 +67,4 @@ object ComposeLogic {
4367

4468
}
4569

46-
case class ComposeLogic[T](operator: String, conditions: Array[JsonLogicCore[_]]) extends JsonLogicCore[T](operator)
70+
case class ComposeLogic(operator: String, conditions: Array[JsonLogicCore]) extends JsonLogicCore(operator)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.github.celadari.jsonlogicscala.core
2+
3+
import play.api.libs.json.{JsValue, Reads}
4+
5+
object Decoder {
6+
implicit val defaultDecoder: Decoder = new Decoder {
7+
override def customDecode(json: JsValue, otherType: String)(implicit reads: Array[Reads[_]] = Array()): Any =
8+
throw new NotImplementedError("You must provide a decoding method for this object.")
9+
}
10+
}
11+
12+
abstract class Decoder {
13+
def customDecode(json: JsValue, otherType: String)(implicit reads: Array[Reads[_]]): Any
14+
15+
def decode(jsValue: JsValue, `type`: String)(implicit reads: Array[Reads[_]] = Array()): ValueLogic[_] = {
16+
val value = `type` match {
17+
case "byte" => jsValue.as[Byte]
18+
case "short" => jsValue.as[Short]
19+
case "int" => jsValue.as[Int]
20+
case "long" => jsValue.as[Long]
21+
case "string" => jsValue.as[String]
22+
case "float" => jsValue.as[Float]
23+
case "double" => jsValue.as[Double]
24+
case "boolean" => jsValue.as[Boolean]
25+
case otherType => customDecode(jsValue, otherType)(reads)
26+
}
27+
ValueLogic("var", value)
28+
}
29+
}

src/main/scala/com/github/celadari/jsonlogicscala/core/JsonLogicCore.scala

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import play.api.libs.json._
44

55
object JsonLogicCore {
66

7-
private[core] def parse[T](jsonLogic: JsObject, jsonLogicData: JsObject)(implicit fmt: Reads[T]): JsonLogicCore[T] = {
7+
private[core] def parse[T](jsonLogic: JsObject, jsonLogicData: JsObject)(implicit fmt: Reads[T]): JsonLogicCore = {
88
// check for operator field
99
val fields = jsonLogic.fields
1010

@@ -20,19 +20,35 @@ object JsonLogicCore {
2020
ComposeLogic.parse[T](jsonLogic, jsonLogicData)(fmt)
2121
}
2222

23-
implicit def jsonLogicCoreReads[T](implicit fmt: Reads[T]): Reads[JsonLogicCore[T]] = new Reads[JsonLogicCore[T]] {
23+
private[core] def decode(jsonLogic: JsObject, jsonLogicData: JsObject)(implicit decoder: Decoder): JsonLogicCore = {
24+
// check for operator field
25+
val fields = jsonLogic.fields
26+
27+
// if operator is data access
28+
if (fields.map(_._1).contains("var")) return ValueLogic.decode(jsonLogic, jsonLogicData)(decoder)
29+
30+
// check for compose logic operator field
31+
if (fields.length > 1) throw new Error("JSON object is supposed to have only one operator field.")
32+
val operator = fields.head._1
33+
if (!ComposeLogic.OPERATORS.contains(operator)) throw new Error(s"Invalid parsed operator: $operator")
34+
35+
// if operator is compose logic
36+
ComposeLogic.decode(jsonLogic, jsonLogicData)(decoder)
37+
}
38+
39+
implicit def jsonLogicCoreReads(implicit decoder: Decoder): Reads[JsonLogicCore] = new Reads[JsonLogicCore] {
2440

25-
override def reads(json: JsValue): JsResult[JsonLogicCore[T]] = {
41+
override def reads(json: JsValue): JsResult[JsonLogicCore] = {
2642

2743
// split json in two components jsonLogic and jsonLogicData
2844
val jsonLogic = (json \ 0).getOrElse(JsObject.empty).asInstanceOf[JsObject]
2945
val jsonLogicData = (json \ 1).getOrElse(JsObject.empty).asInstanceOf[JsObject]
3046

3147
// apply reading with distinguished components: logic and data
32-
JsSuccess(parse(jsonLogic, jsonLogicData))
48+
JsSuccess(decode(jsonLogic, jsonLogicData)(decoder))
3349
}
3450
}
3551
}
3652

3753

38-
abstract class JsonLogicCore[T](operator: String)
54+
abstract class JsonLogicCore(operator: String)

src/main/scala/com/github/celadari/jsonlogicscala/core/ValueLogic.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@ object ValueLogic {
99
ValueLogic("var", (jsonLogicData \ pathData).as[T])
1010
}
1111

12+
private[core] def decode(jsonLogic: JsObject, jsonLogicData: JsObject)(implicit decoder: Decoder): ValueLogic[_] = {
13+
val typeData = (jsonLogic \ "type").as[String]
14+
val pathData = (jsonLogic \ "var").as[String]
15+
val json = (jsonLogicData \ pathData).get
16+
ValueLogic("var", decoder.decode(json, typeData))
17+
}
18+
1219
implicit def valueLogicReads[T](implicit fmt: Reads[T]): Reads[ValueLogic[T]] = new Reads[ValueLogic[T]] {
1320
override def reads(json: JsValue): JsResult[ValueLogic[T]] = {
1421
JsSuccess(ValueLogic("var", (json \ "var").as[T]))
1522
}
1623
}
1724
}
1825

19-
case class ValueLogic[T](operator: String, value: T) extends JsonLogicCore[T](operator)
26+
case class ValueLogic[T](operator: String, value: T) extends JsonLogicCore(operator)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.github.celadari.jsonlogicscala.operators
2+
3+
4+
object HasCompareMethod {
5+
6+
abstract class HasCompareMethod[A](a: A) {
7+
def <[B](b: B): Boolean
8+
def >[B](b: B): Boolean = ! <=(b)
9+
def <=[B](b: B): Boolean = <(b) || a == b
10+
def >=[B](b: B): Boolean = >(b) || a == b
11+
}
12+
13+
implicit class ByteComparator(a: Byte) extends HasCompareMethod[Byte](a){
14+
def <[B](b: B): Boolean = a < b
15+
}
16+
17+
implicit class ShortComparator(a: Short) extends HasCompareMethod[Short](a){
18+
def <[B](b: B): Boolean = a < b
19+
}
20+
21+
implicit class IntComparator(a: Int) extends HasCompareMethod[Int](a){
22+
def <[B](b: B): Boolean = a < b
23+
}
24+
25+
implicit class FloatComparator(a: Float) extends HasCompareMethod[Float](a){
26+
def <[B](b: B): Boolean = a < b
27+
}
28+
29+
implicit class DoubleComparator(a: Double) extends HasCompareMethod[Double](a){
30+
def <[B](b: B): Boolean = a < b
31+
}
32+
33+
implicit class LongComparator(a: Long) extends HasCompareMethod[Long](a){
34+
def <[B](b: B): Boolean = a < b
35+
}
36+
37+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.github.celadari.jsonlogicscala.operators
2+
3+
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
4+
import java.lang.CharSequence
5+
6+
object HasContainsMethod {
7+
abstract class HasContainsMethod[A](a: A) {
8+
def contains[B](b: B): Boolean
9+
}
10+
11+
implicit class ArrayHasContainsMethod(arr: Array[_]) extends HasContainsMethod[Array[_]](arr) {
12+
def contains[B](b: B): Boolean = arr.contains(b)
13+
}
14+
15+
implicit class ArrayBufferHasContainsMethod(arr: ArrayBuffer[_]) extends HasContainsMethod[ArrayBuffer[_]](arr) {
16+
def contains[B](b: B): Boolean = arr.contains(b)
17+
}
18+
19+
implicit class ListHasContainsMethod(arr: List[_]) extends HasContainsMethod[List[_]](arr) {
20+
def contains[B](b: B): Boolean = arr.contains(b)
21+
}
22+
23+
implicit class ListBufferHasContainsMethod(arr: ListBuffer[_]) extends HasContainsMethod[ListBuffer[_]](arr) {
24+
def contains[B](b: B): Boolean = arr.contains(b)
25+
}
26+
27+
implicit class StringBuilderHasContainsMethod(arr: StringBuilder) extends HasContainsMethod[StringBuilder](arr) {
28+
def contains[B](b: B): Boolean = arr.contains(b)
29+
}
30+
31+
implicit class VectorHasContainsMethod(arr: Vector[_]) extends HasContainsMethod[Vector[_]](arr) {
32+
def contains[B](b: B): Boolean = arr.contains(b)
33+
}
34+
35+
implicit class MutableStackHasContainsMethod(arr: scala.collection.mutable.Stack[_]) extends HasContainsMethod[scala.collection.mutable.Stack[_]](arr) {
36+
def contains[B](b: B): Boolean = arr.contains(b)
37+
}
38+
39+
implicit class ImmutableQueueHasContainsMethod(arr: scala.collection.immutable.Queue[_]) extends HasContainsMethod[scala.collection.immutable.Queue[_]](arr) {
40+
def contains[B](b: B): Boolean = arr.contains(b)
41+
}
42+
43+
implicit class MutableQueueHasContainsMethod(arr: scala.collection.mutable.Queue[_]) extends HasContainsMethod[scala.collection.mutable.Queue[_]](arr) {
44+
def contains[B](b: B): Boolean = arr.contains(b)
45+
}
46+
47+
implicit class RangeHasContainsMethod(arr: Range) extends HasContainsMethod[Range](arr) {
48+
def contains[B](b: B): Boolean = arr.contains(b)
49+
}
50+
51+
52+
}

0 commit comments

Comments
 (0)