Skip to content

Instantly share code, notes, and snippets.

@Narigo
Created October 2, 2013 17:29
Show Gist options
  • Save Narigo/6797285 to your computer and use it in GitHub Desktop.
Save Narigo/6797285 to your computer and use it in GitHub Desktop.
Making json API typesafe... Is this a good idea?
diff --git a/src/main/scala/org/vertx/scala/core/json/Json.scala b/src/main/scala/org/vertx/scala/core/json/Json.scala
index d756a32..b6d32d5 100644
--- a/src/main/scala/org/vertx/scala/core/json/Json.scala
+++ b/src/main/scala/org/vertx/scala/core/json/Json.scala
@@ -17,51 +17,6 @@
import scala.annotation.implicitNotFound
-@implicitNotFound(msg = "Cannot find add operations for type ${T}")
-trait JsonElemOps[T] {
- def addToObj(o: JsonObject, key: String, v: T): JsonObject
- def addToArr(a: JsonArray, v: T): JsonArray
-}
-
-object JsonElemOps {
- implicit object JsonStringElem extends JsonElemOps[String] {
- def addToObj(o: JsonObject, key: String, v: String): JsonObject = o.putString(key, v)
- def addToArr(a: JsonArray, v: String): JsonArray = a.addString(v)
- }
- implicit object JsonIntElem extends JsonElemOps[Int] {
- def addToObj(o: JsonObject, key: String, v: Int): JsonObject = o.putNumber(key, v)
- def addToArr(a: JsonArray, v: Int): JsonArray = a.addNumber(v)
- }
- implicit object JsonBoolElem extends JsonElemOps[Boolean] {
- def addToObj(o: JsonObject, key: String, v: Boolean): JsonObject = o.putBoolean(key, v)
- def addToArr(a: JsonArray, v: Boolean): JsonArray = a.addBoolean(v)
- }
- implicit object JsonFloatElem extends JsonElemOps[Float] {
- def addToObj(o: JsonObject, key: String, v: Float): JsonObject = o.putNumber(key, v)
- def addToArr(a: JsonArray, v: Float): JsonArray = a.addNumber(v)
- }
- implicit object JsonJsObjectElem extends JsonElemOps[JsonObject] {
- def addToObj(o: JsonObject, key: String, v: JsonObject): JsonObject = o.putObject(key, v)
- def addToArr(a: JsonArray, v: JsonObject): JsonArray = a.addObject(v)
- }
- implicit object JsonJsArrayElem extends JsonElemOps[JsonArray] {
- def addToObj(o: JsonObject, key: String, v: JsonArray): JsonObject = o.putArray(key, v)
- def addToArr(a: JsonArray, v: JsonArray): JsonArray = a.addArray(v)
- }
- implicit object JsonJsElem extends JsonElemOps[JsonElement] {
- def addToObj(o: JsonObject, key: String, v: JsonElement): JsonObject = o.putElement(key, v)
- def addToArr(a: JsonArray, v: JsonElement): JsonArray = a.addElement(v)
- }
- implicit object JsonBinaryElem extends JsonElemOps[Array[Byte]] {
- def addToObj(o: JsonObject, key: String, v: Array[Byte]): JsonObject = o.putBinary(key, v)
- def addToArr(a: JsonArray, v: Array[Byte]): JsonArray = a.addBinary(v)
- }
- implicit object JsonAnyElem extends JsonElemOps[Any] {
- def addToObj(o: JsonObject, key: String, v: Any): JsonObject = o.putValue(key, v)
- def addToArr(a: JsonArray, v: Any): JsonArray = a.add(v)
- }
-}
-
/**
* Helper to construct JsonObjects and JsonArrays.
*
@@ -76,7 +31,7 @@
* @param fields The fieldName -> value pairs
* @return A JsonObject containing the name -> value pairs.
*/
- def apply(fields: (String, Any)*): JsonObject = obj(fields: _*)
+ def apply(fields: (String, JsonElemOps[_])*): JsonObject = obj(fields: _*)
/**
* Creates a JsonArray from a sequence of values.
@@ -84,7 +39,7 @@
* @param elements The elements to put into the JsonArray.
* @return A JsonArray containing the provided elements.
*/
- def apply(elements: Seq[Any]): JsonArray = arr(elements)
+ def apply(elements: JsonElemOps[_]*): JsonArray = arr(elements: _*)
/**
* Creates a JsonArray from an encoded JSON string.
@@ -121,9 +76,9 @@
* @param fields The fieldName -> value pairs
* @return A JsonObject containing the name -> value pairs.
*/
- def obj(fields: (String, Any)*): JsonObject = {
+ def obj(fields: (String, JsonElemOps[_])*): JsonObject = {
val o = new JsonObject()
- fields.foreach(f => addToObject(o, f._1, f._2))
+ fields.foreach(f => f._2.addToObj(o, f._1))
o
}
@@ -133,18 +88,11 @@
* @param elements The elements to put into the JsonArray.
* @return A JsonArray containing the provided elements.
*/
- def arr(fields: Seq[Any]): JsonArray = {
+ def arr(fields: JsonElemOps[_]*): JsonArray = {
val a = new JsonArray()
- fields.foreach(f => addToArray(a, f))
+ fields.foreach(f => f.addToArr(a))
a
}
- private def addToArray[T: JsonElemOps](a: JsonArray, fieldValue: T) = {
- implicitly[JsonElemOps[T]].addToArr(a, fieldValue)
- }
-
- private def addToObject[T: JsonElemOps](o: JsonObject, fieldName: String, fieldValue: T) = {
- implicitly[JsonElemOps[T]].addToObj(o, fieldName, fieldValue)
- }
-
+ def listToArray(fields: Seq[JsonElemOps[_]]): JsonArray = arr(fields: _*)
}
\ No newline at end of file
diff --git a/src/main/scala/org/vertx/scala/core/json/package.scala b/src/main/scala/org/vertx/scala/core/json/package.scala
index 865db88..0cacdb9 100644
--- a/src/main/scala/org/vertx/scala/core/json/package.scala
+++ b/src/main/scala/org/vertx/scala/core/json/package.scala
@@ -16,6 +16,7 @@
package org.vertx.scala.core
import scala.collection.mutable.Map
+import scala.annotation.implicitNotFound
/**
* @author swilliams
@@ -37,4 +38,56 @@
def asMap: Map[String, AnyRef] = internal.toMap.asScala
}
+ @implicitNotFound(msg = "Cannot find add operations for type ${T}")
+ trait JsonElemOps[T] {
+ def addToObj(o: JsonObject, key: String): JsonObject
+ def addToArr(a: JsonArray): JsonArray
+ val data: T
+ }
+
+ implicit class JsonStringElem(val data: String) extends JsonElemOps[String] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putString(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addString(data)
+ }
+ implicit class JsonIntElem(val data: Int) extends JsonElemOps[Int] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putNumber(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addNumber(data)
+ }
+ implicit class JsonBoolElem(val data: Boolean) extends JsonElemOps[Boolean] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putBoolean(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addBoolean(data)
+ }
+ implicit class JsonDoubleElem(val data: Double) extends JsonElemOps[Double] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putNumber(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addNumber(data)
+ }
+ implicit class JsonLongElem(val data: Long) extends JsonElemOps[Long] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putNumber(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addNumber(data)
+ }
+ implicit class JsonFloatElem(val data: Float) extends JsonElemOps[Float] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putNumber(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addNumber(data)
+ }
+ implicit class JsonJsObjectElem(val data: JsonObject) extends JsonElemOps[JsonObject] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putObject(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addObject(data)
+ }
+ implicit class JsonJsArrayElem(val data: JsonArray) extends JsonElemOps[JsonArray] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putArray(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addArray(data)
+ }
+ implicit class JsonJsElem(val data: JsonElement) extends JsonElemOps[JsonElement] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putElement(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addElement(data)
+ }
+ implicit class JsonBinaryElem(val data: Array[Byte]) extends JsonElemOps[Array[Byte]] {
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putBinary(key, data)
+ def addToArr(a: JsonArray): JsonArray = a.addBinary(data)
+ }
+ // implicit object JsonAnyElem extends JsonElemOps[Any] {
+ // def addToObj(o: JsonObject, key: String, v: Any): JsonObject = o.putValue(key, v)
+ // def addToArr(a: JsonArray, v: Any): JsonArray = a.add(v)
+ // }
+
}
\ No newline at end of file
diff --git a/src/test/scala/org/vertx/scala/tests/core/eventbus/EventBusTest.scala b/src/test/scala/org/vertx/scala/tests/core/eventbus/EventBusTest.scala
index 37a12c8..89109f1 100644
--- a/src/test/scala/org/vertx/scala/tests/core/eventbus/EventBusTest.scala
+++ b/src/test/scala/org/vertx/scala/tests/core/eventbus/EventBusTest.scala
@@ -4,7 +4,7 @@
import org.vertx.scala.testtools.TestVerticle
import org.vertx.testtools.VertxAssert._
import org.vertx.scala.core.eventbus.Message
-import org.vertx.scala.core.json.Json
+import org.vertx.scala.core.json._
import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicInteger
import org.vertx.scala.core.json.JsonObject
diff --git a/src/test/scala/org/vertx/scala/tests/core/json/JsonTest.scala b/src/test/scala/org/vertx/scala/tests/core/json/JsonTest.scala
index 55a6619..1255455 100644
--- a/src/test/scala/org/vertx/scala/tests/core/json/JsonTest.scala
+++ b/src/test/scala/org/vertx/scala/tests/core/json/JsonTest.scala
@@ -34,15 +34,13 @@
"foo" -> "foo text",
"bar" -> 3.45d,
"baz" -> false,
- "myInt" -> Integer.MAX_VALUE
- )
+ "myInt" -> Integer.MAX_VALUE)
assertEquals("foo text", obj.getString("foo"))
assertEquals(3.45d, obj.getValue[Double]("bar"), 1e-15)
assertEquals(false, obj.getBoolean("baz"))
assertEquals(Integer.MAX_VALUE, obj.getInteger("myInt"))
assertEquals(enc, obj.encode())
-
}
@@ -55,7 +53,7 @@
val scalaMapFields: scala.collection.Map[String, AnyRef] = wrapped.asMap
//Get the original object
- val obj2:JsonObject = wrapped
+ val obj2: JsonObject = wrapped
assertEquals("foo text", scalaMapFields("foo"))
assertEquals(true, obj2.getBoolean("optional"))
@@ -66,85 +64,66 @@
def jsonArrayTest() {
val enc = """["f",3,"b",7,35.4,true]"""
- val array = Json.arr(List("f", 3, "b", 7, 35.4f, true))
+ val array = Json.arr("f", 3, "b", 7, 35.4f, true)
assertEquals(6, array.size())
assertEquals(enc, array.encode())
}
-
@Test
- def customObjTest(){
+ def customObjTest() {
import java.util.Date
- case class Custom(date:Date, other:Boolean)
+ case class Custom(date: Date, other: Boolean)
+ implicit class customObj(val data: Custom) extends JsonElemOps[Custom] {
+ def addToArr(a: JsonArray): JsonArray = a.add(data)
+ def addToObj(o: JsonObject, key: String): JsonObject = o.putValue(key, data)
+ }
+
val info = Custom(new Date(), false)
- val obj1 = Json.obj("custom" -> info)
+ val obj1 = Json.obj("custom" -> info)
assertEquals(info, obj1.getValue[Custom]("custom"))
}
@Test
- def nestedObjectsTest(){
+ def nestedObjectsTest() {
val obj =
Json.obj(
- "webappconf" -> Json.obj(
- "port" -> 8080,
- "ssl" -> false,
- "bridge" -> true,
- "inbound_permitted" -> Json.arr(
- Seq(Json.obj(
- "address" -> "acme.bar",
- "match" -> Json.obj(
- "action" -> "foo"
- )),
- Json.obj(
- "address" -> "acme.baz",
- "match" -> Json.obj(
- "action" -> "index"
- ))
- )
- ),
- "outbound_permitted" -> Json.arr(
- Seq(new JsonObject())
- )
- )
- )
+ "webappconf" -> Json.obj(
+ "port" -> 8080,
+ "ssl" -> false,
+ "bridge" -> true,
+ "inbound_permitted" -> Json.arr(
+ Json.obj(
+ "address" -> "acme.bar",
+ "match" -> Json.obj("action" -> "foo")),
+ Json.obj(
+ "address" -> "acme.baz",
+ "match" -> Json.obj("action" -> "index"))),
+ "outbound_permitted" -> Json.arr(new JsonObject())))
assertEquals(jsonString, obj.encode())
}
-
@Test
- def nestedObjectsShorterVersionTest(){
- val obj =
- Json(
- "webappconf" -> Json(
- "port" -> 8080,
- "ssl" -> false,
- "bridge" -> true,
- "inbound_permitted" -> Json(
- Seq(Json(
- "address" -> "acme.bar",
- "match" -> Json(
- "action" -> "foo"
- )),
- Json(
- "address" -> "acme.baz",
- "match" -> Json(
- "action" -> "index"
- ))
- )
- ),
- "outbound_permitted" -> Json(
- Seq(new JsonObject())
- )
- )
- )
+ def nestedObjectsShorterVersionTest() {
+ val obj = Json.obj(
+ "webappconf" -> Json.obj(
+ "port" -> 8080,
+ "ssl" -> false,
+ "bridge" -> true,
+ "inbound_permitted" -> Json(
+ Json.obj(
+ "address" -> "acme.bar",
+ "match" -> Json.obj("action" -> "foo")),
+ Json.obj(
+ "address" -> "acme.baz",
+ "match" -> Json.obj("action" -> "index")))),
+ "outbound_permitted" -> Json(Json.obj()))
assertEquals(jsonString, obj.encode())
}
-
private def jsonString = {
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment