Permalink
Browse files

Added JSON support for BSONTimestamp type.

  • Loading branch information...
1 parent 5e2713b commit 388f99de180a90a5b069eab11e15756e5d932808 @rajish rajish committed Aug 28, 2012
View
@@ -12,4 +12,5 @@ java_pid*.hprof
tags
.ensime
*.iml
-.history
+.history
+*~
@@ -30,14 +30,15 @@ import org.joda.time.format.{ DateTimeFormatter, ISODateTimeFormat }
import java.util.Date
import org.scala_tools.time.Imports
import net.liftweb.json.JsonAST._
-import org.bson.types.ObjectId
+import org.bson.types.{ BSONTimestamp, ObjectId }
object JSONConfig {
val ISO8601 = ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC)
}
case class JSONConfig(dateStrategy: JSONDateStrategy = StringDateStrategy(),
objectIdStrategy: JSONObjectIdStrategy = StrictJSONObjectIdStrategy,
+ bsonTimestampStrategy: JSONbsTimesampStrategy = StrictBSONTimestampStrategy,
outputNullValues: Boolean = false)
trait JSONObjectIdStrategy {
@@ -112,4 +113,21 @@ case class StrictJSONDateStrategy(zone: DateTimeZone = DateTimeZone.UTC) extends
}
}
-// or roll your own date strategy.... O the excitement.
+trait JSONbsTimesampStrategy {
+ def in(j: JValue): BSONTimestamp
+
+ def out(ts: BSONTimestamp): JValue
+
+ protected def unexpectedInput(x: JValue) =
+ sys.error("in: unexpected OID input class='%s', value='%s'".format(x.getClass.getName, x.values))
+}
+
+object StrictBSONTimestampStrategy extends JSONbsTimesampStrategy {
+ def in(j: JValue) = j match {
+ case JObject(JField("$ts", JInt(ts)) :: JField("$inc", JInt(inc)) :: Nil) => new BSONTimestamp(ts.toInt, inc.toInt)
+ case x => unexpectedInput(x)
+ }
+
+ def out(ts: BSONTimestamp) = JObject(List(JField("$ts", JInt(ts.getTime)), JField("$inc", JInt(ts.getInc))))
+}
+// or roll your own date strategy.... O the excitement.
@@ -33,6 +33,7 @@ import java.net.URL
import com.novus.salat.TypeFinder
import com.novus.salat.StringTypeHintStrategy
import scala.tools.scalap.scalax.rules.scalasig.TypeRefType
+import org.bson.types.BSONTimestamp
object ToJField extends Logging {
def typeHint[X](clazz: Class[X], useTypeHint: Boolean)(implicit ctx: Context) = {
@@ -79,6 +80,7 @@ object ToJValue extends Logging {
case o: ObjectId => ctx.jsonConfig.objectIdStrategy.out(o)
case u: java.net.URL => JString(u.toString) // might as well
case n if n == null && ctx.jsonConfig.outputNullValues => JNull
+ case ts: BSONTimestamp => ctx.jsonConfig.bsonTimestampStrategy.out(ts)
case x: AnyRef => sys.error("serialize: Unsupported JSON transformation for class='%s', value='%s'".format(x.getClass.getName, x))
}
@@ -124,6 +126,7 @@ object FromJValue extends Logging {
}
case o: JObject if field.tf.isOid => deserialize(o, field.tf)
case o: JObject if field.tf.isDate || field.tf.isDateTime => deserialize(o, field.tf)
+ case o: JObject if field.tf.isBSONTimestamp => deserialize(o, field.tf)
case o: JObject => ctx.lookup(if (childType.isDefined) childType.get.symbol.path else field.typeRefType.symbol.path).fromJSON(o)
case x => deserialize(x, if (childType.isDefined) TypeFinder(childType.get) else field.tf)
}
@@ -133,6 +136,7 @@ object FromJValue extends Logging {
case v if tf.isDateTime => ctx.jsonConfig.dateStrategy.toDateTime(v)
case v if tf.isDate => ctx.jsonConfig.dateStrategy.toDate(v)
case v if tf.isOid => ctx.jsonConfig.objectIdStrategy.in(v)
+ case v if tf.isBSONTimestamp => ctx.jsonConfig.bsonTimestampStrategy.in(v)
case s: JString if tf.isChar => s.values.charAt(0)
case s: JString if tf.isURL => new URL(s.values)
case s: JString => s.values
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2010 - 2012 Novus Partners, Inc. (http://www.novus.com)
+ *
+ * Module: salat-core
+ * Class: DateStrategySpec.scala
+ * Last modified: 2012-06-28 15:37:34 EDT
+ *
+ * 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.
+ *
+ * Project: http://github.com/novus/salat
+ * Wiki: http://github.com/novus/salat/wiki
+ * Mailing list: http://groups.google.com/group/scala-salat
+ */
+
+package com.novus.salat.test.json
+
+import org.specs2.mutable.Specification
+import com.novus.salat.util.Logging
+import org.joda.time.{ DateTimeZone, DateTime }
+import com.novus.salat.json.StrictBSONTimestampStrategy
+import net.liftweb.json.JsonAST.{ JField, JObject, JInt, JString }
+import org.bson.types.BSONTimestamp
+
+class BSONTimestampStrategySpec extends Specification with Logging {
+
+ val ts = new BSONTimestamp(1345193830, 10)
+ val formatted = "{\"$ts\" : 1345193830 , \"$inc\" : 10}"
+
+ "JSON BSONTimestamp strategy " should {
+ "Strict JSON" in {
+ val s = StrictBSONTimestampStrategy
+ val j = JObject(JField("$ts", JInt(1345193830)) :: (JField("$inc", JInt(10))) :: Nil)
+
+ "from BSONTimestamp to JSON" in {
+ s.out(ts) must_== j
+ }
+ "from JSON to BSONTimestamp" in {
+ s.in(j) must_== ts
+ }
+ "throw an error when an unexpected JSON field type is submitted" in {
+ s.in(j \ "$ts") must throwA[RuntimeException]
+ }
+ }
+ }
+}
@@ -30,6 +30,7 @@ protected[salat] object Types {
val Date = "java.util.Date"
val DateTime = Set("org.joda.time.DateTime", "org.scala_tools.time.TypeImports.DateTime")
val Oid = Set("org.bson.types.ObjectId", "com.mongodb.casbah.commons.TypeImports.ObjectId")
+ val BsonTimestamp = "org.bson.types.BSONTimestamp"
val SBigDecimal = classOf[SBigDecimal].getName
val BigInt = classOf[BigInt].getName
val Option = "scala.Option"
@@ -64,6 +65,7 @@ protected[salat] case class TypeFinder(t: TypeRefType) {
lazy val isOption = TypeMatchers.matches(t, Types.Option)
lazy val isOid = TypeMatchers.matches(t, Types.Oid)
lazy val isURL = TypeMatchers.matches(t, classOf[java.net.URL].getName)
+ lazy val isBSONTimestamp = TypeMatchers.matches(t, Types.BsonTimestamp)
}
protected[salat] object TypeMatchers {
@@ -102,4 +104,4 @@ protected[salat] object IsTraversable {
protected[salat] object IsScalaBigDecimal {
def unapply(t: Type): Option[Type] = TypeMatchers.matchesOneType(t, Types.SBigDecimal)
-}
+}

0 comments on commit 388f99d

Please sign in to comment.