Created
November 21, 2011 06:10
-
-
Save berngp/1381804 to your computer and use it in GitHub Desktop.
Lift MongoMapField with support of embedded collections.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.ign.lift.record | |
import java.util.{Calendar, Date, GregorianCalendar, UUID} | |
import java.util.regex.Pattern | |
import net.liftweb.json.JsonAST._ | |
import net.liftweb.json.Formats | |
import com.mongodb.{BasicDBList, BasicDBObject, DBRef} | |
import scala.collection.JavaConversions._ | |
import org.bson.types.ObjectId | |
private[record] object Meta { | |
/* | |
* For converting scala objects into DBObject values | |
*/ | |
object Reflection { | |
import math.BigInt | |
/* | |
* These don't require a conversion and can be put directly into a DBObject | |
*/ | |
val primitives = Set[Class[_]]( | |
classOf[String], classOf[Int], classOf[Long], classOf[Double], | |
classOf[Float], classOf[Byte], classOf[BigInt], classOf[Boolean], | |
classOf[Short], classOf[java.lang.Integer], classOf[java.lang.Long], | |
classOf[java.lang.Double], classOf[java.lang.Float], | |
classOf[java.lang.Byte], classOf[java.lang.Boolean], | |
classOf[java.lang.Short]) | |
def primitive_?(clazz: Class[_]) = primitives contains clazz | |
/* | |
* This is used to convert DBObjects into JObjects | |
*/ | |
def primitive2jvalue(a: Any) = a match { | |
case x: String => JString(x) | |
case x: Int => JInt(x) | |
case x: Long => JInt(x) | |
case x: Double => JDouble(x) | |
case x: Float => JDouble(x) | |
case x: Byte => JInt(BigInt(x)) | |
case x: BigInt => JInt(x) | |
case x: Boolean => JBool(x) | |
case x: Short => JInt(BigInt(x)) | |
case x: java.lang.Integer => JInt(BigInt(x.asInstanceOf[Int])) | |
case x: java.lang.Long => JInt(BigInt(x.asInstanceOf[Long])) | |
case x: java.lang.Double => JDouble(x.asInstanceOf[Double]) | |
case x: java.lang.Float => JDouble(x.asInstanceOf[Float]) | |
case x: java.lang.Byte => JInt(BigInt(x.asInstanceOf[Byte])) | |
case x: java.lang.Boolean => JBool(x.asInstanceOf[Boolean]) | |
case x: java.lang.Short => JInt(BigInt(x.asInstanceOf[Short])) | |
case _ => sys.error("not a primitive " + a.asInstanceOf[AnyRef].getClass) | |
} | |
/* | |
* Date types require formatting | |
*/ | |
val datetypes = Set[Class[_]]( | |
classOf[Calendar], classOf[Date], classOf[GregorianCalendar]) | |
def datetype_?(clazz: Class[_]) = datetypes contains clazz | |
def datetype2jvalue(a: Any)(implicit formats: Formats) = a match { | |
case x: Calendar => dateAsJValue(x.getTime, formats) | |
case x: Date => dateAsJValue(x, formats) | |
} | |
def datetype2dbovalue(a: Any) = a match { | |
case x: Calendar => x.getTime | |
case x: Date => x | |
} | |
/* | |
* Extended Mongo types. | |
*/ | |
val mongotypes = Set[Class[_]]( | |
classOf[DBRef], classOf[ObjectId], classOf[Pattern], classOf[UUID], classOf[BasicDBList], classOf[BasicDBObject]) | |
def mongotype_?(clazz: Class[_]) = mongotypes contains clazz | |
/* | |
* Definitive place for JValue conversion of mongo types | |
*/ | |
def mongotype2jvalue(a: Any)(implicit formats: Formats): JValue = a match { | |
case x: ObjectId => objectIdAsJValue(x, formats) | |
case x: Pattern => patternAsJValue(x) | |
case x: UUID => uuidAsJValue(x) | |
case x: BasicDBList => basicDBListAsJValue(x) | |
case x: BasicDBObject => basicDBObjectAsJValue(x) | |
case x: DBRef => sys.error("DBRefs are not supported.") | |
case _ => sys.error("not a mongotype " + a.asInstanceOf[AnyRef].getClass) | |
} | |
/** | |
* Scala & Java Lists and Maps | |
*/ | |
val langCollections = Set[Class[_]]( | |
classOf[List[_]], | |
classOf[Map[String, _]], | |
classOf[java.util.List[_]], | |
classOf[java.util.Map[_, _]]) | |
def langCollection_?(clazz: Class[_]) = langCollections.find { | |
c: Class[_] => c.isAssignableFrom(clazz) | |
}.isDefined | |
/* | |
* This is used to convert List and Collections to JObjects. | |
*/ | |
def collection2jvalue(a: Any)(implicit formats: Formats): JValue = a match { | |
case x: List[_] => scalaListAsJValue(x) | |
case x: java.util.List[_] => scalaListAsJValue(x.toList) | |
case x: Map[_, _] => scalaMapAsJValue(x) | |
case x: java.util.Map[_, _] => scalaMapAsJValue(x) | |
case _ => sys.error("not an identifiable collection " + a.asInstanceOf[AnyRef].getClass) | |
} | |
def scalaListAsJValue(a: List[_])(implicit formats: Formats) = JArray(a.map(_asJValue(_, formats))) | |
def scalaMapAsJValue(a: scala.collection.Map[_, _])(implicit formats: Formats) = JObject( | |
a.map(t => JField(t._1.toString, _asJValue(t._2, formats))).toList) | |
} | |
def dateAsJValue(d: Date, formats: Formats) = JObject(JField("$dt", JString(formats.dateFormat.format(d))) :: Nil) | |
def objectIdAsJValue(oid: ObjectId): JValue = JObject(JField("$oid", JString(oid.toString)) :: Nil) | |
def patternAsJValue(p: Pattern): JValue = JObject(JField("$regex", JString(p.pattern)) :: JField("$flags", JInt(p.flags)) :: Nil) | |
def uuidAsJValue(u: UUID): JValue = JObject(JField("$uuid", JString(u.toString)) :: Nil) | |
def objectIdAsJValue(oid: ObjectId, formats: Formats): JValue = | |
if (isObjectIdSerializerUsed(formats)) | |
objectIdAsJValue(oid) | |
else | |
JString(oid.toString) | |
def basicDBListAsJValue(a: BasicDBList)(implicit formats: Formats) = JArray( | |
a.iterator().toList.map(_asJValue(_, formats))) | |
def basicDBObjectAsJValue(a: BasicDBObject)(implicit formats: Formats) = JObject( | |
a.iterator.toList.map(t => JField(t._1, _asJValue(t._2, formats)))) | |
def _asJValue(value: Any, formats: Formats): JValue = value match { | |
case x if Reflection.primitive_?(x.getClass) => Reflection.primitive2jvalue(x) | |
case x if Reflection.mongotype_?(x.getClass) => Reflection.mongotype2jvalue(x)(formats) | |
case x if Reflection.datetype_?(x.getClass) => Reflection.datetype2jvalue(x)(formats) | |
case x if Reflection.langCollection_?(x.getClass) => Reflection.collection2jvalue(x)(formats) | |
case _ => JNothing | |
} | |
/* | |
* Check to see if the ObjectIdSerializer is being used. | |
*/ | |
private def isObjectIdSerializerUsed(formats: Formats): Boolean = | |
formats.customSerializers.exists(_.getClass == objectIdSerializerClass) | |
private val objectIdSerializerClass = classOf[net.liftweb.mongodb.ObjectIdSerializer] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.ign.lift.record | |
import net.liftweb.mongodb.record.field.MongoMapField | |
import net.liftweb.mongodb.record.BsonRecord | |
import net.liftweb.common.{Empty, Full} | |
import net.liftweb.json.JsonAST._ | |
import net.liftweb.record._ | |
class MongoMapField_V1[OwnerType <: BsonRecord[OwnerType], MapValueType](rec: OwnerType) | |
extends MongoMapField[OwnerType, MapValueType](rec) { | |
override def setFromJValue(jvalue: JValue) = jvalue match { | |
case JNothing | JNull if optional_? => setBox(Empty) | |
case JObject(obj) => setBox(Full( | |
Map() ++ obj.map(jf => (jf.name, valueExtractor(jf.value))) | |
)) | |
case other => setBox(FieldHelpers.expectedA("JObject", other)) | |
} | |
protected def valueExtractor(value: JValue): MapValueType = (value match { | |
case jint: JInt => mapValueTypeExtractor(jint.values) | |
case jarray: JArray => | |
val _list = new java.util.LinkedList[Any]() | |
jarray.values.foreach(_v => _list.add(mapValueTypeExtractor(_v))) | |
java.util.Collections.unmodifiableList(_list).asInstanceOf[MapValueType] | |
case jobject: JObject => | |
val _map: java.util.Map[String, Any] = new java.util.LinkedHashMap() | |
jobject.values.foreach(t => _map.put(t._1, mapValueTypeExtractor(t._2))) | |
java.util.Collections.unmodifiableMap[String, Any](_map).asInstanceOf[MapValueType] | |
case _ => value.values.asInstanceOf[MapValueType] | |
}).asInstanceOf[MapValueType] | |
protected def mapValueTypeExtractor(value: Any): Any = (value match { | |
case n: BigInt => n.intValue() | |
case n: JInt => mapValueTypeExtractor(n.values) | |
case arr: JArray => | |
val _list = new java.util.LinkedList[Any]() | |
arr.values.foreach(_v => _list.add(mapValueTypeExtractor(_v))) | |
java.util.Collections.unmodifiableList(_list) | |
case obj: JObject => | |
val _map: java.util.Map[String, Any] = new java.util.LinkedHashMap() | |
obj.values.foreach(t => _map.put(t._1, mapValueTypeExtractor(t._2))) | |
java.util.Collections.unmodifiableMap[String, Any](_map) | |
case list: List[_] => | |
val _list = new java.util.LinkedList[Any]() | |
list.asInstanceOf[List[Any]].foreach(t => _list.add(mapValueTypeExtractor(t))) | |
java.util.Collections.unmodifiableList(_list) | |
case map: Map[_, _] => | |
val _map: java.util.Map[String, Any] = new java.util.LinkedHashMap[String, Any]() | |
map.asInstanceOf[Map[String, Any]].foreach(t => _map.put(t._1, mapValueTypeExtractor(t._2))) | |
java.util.Collections.unmodifiableMap[String, Any](_map) | |
case _ => value | |
}).asInstanceOf[MapValueType] | |
override def asJValue = JObject(value.keys.map { | |
key => JField(key, value(key).asInstanceOf[AnyRef] match { | |
case null => JNull | |
case aValue: Any => Meta._asJValue(aValue, owner.meta.formats) | |
}) | |
}.toList) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.ign.lift.record | |
import net.liftweb.mongodb.record.BsonRecord | |
import net.liftweb.json.JsonAST._ | |
import collection.immutable.TreeSet | |
class SortableMongoMapField[OwnerType <: BsonRecord[OwnerType], MapValueType](rec: OwnerType) | |
extends MongoMapField_V1[OwnerType, MapValueType](rec) { | |
override def asJValue = JObject( sortKeys(value.keys).map { | |
key => JField(key, value(key).asInstanceOf[AnyRef] match { | |
case null => JNull | |
case aValue: Any => Meta._asJValue(aValue, owner.meta.formats) | |
}) | |
}.toList) | |
def sortKeys(keys:Iterable[String]) = (TreeSet()(Ordering.String) ++ keys) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Bernardo,
I'm using Lift to GET some json and casbah post it to a mongo db like so:
tryo(parse(request.body)) match {
...
val linkedHash = new util.LinkedHashMap [String, Any]
linkedHash.putAll(JavaConversions.mapAsJavaMap(gameJson.values))
val dbobj = new BasicDBObject(gameJson.values)
val collection = db.getCollection("buildmodel")
collection.save(dbobj)
When I try to serialize Int fields from the json, I'm getting the same BigInt exception (when trying to use casbah to save a map to the database) as you did in your thread: https://groups.google.com/forum/?fromgroups#!topic/liftweb/8dRa6cp9VEg
My reason for using Casbah btw is because I need to store embedded documents and lift doesn't support this. I.E. MongoMapField(String, String) would work, but I would need to store a MongoMapField(String, Map). And Casbah doesn't care how nested the map is that you want to store to the db.
I guess I have two questions for you:
Thanks,
Nick