简体   繁体   中英

Scala Lift - Mongodb save data as json object

I have the following model

class Recording private() extends MongoRecord[Recording] with ObjectIdPk[Recording] {

    def meta = Recording

    object data extends StringField(this, 50)

}

I'm currently saving a Json object as a string in the "data" field, I've used JsonObject field before but only with predefined object structures. In this case the json object being saved can have any structure or data fields so a predefined data structure is not an option.

Say I have:

{"name" : "James", "value" : "Hai!"}

Or

{"result" : 1, "handle" : "lorem_ipsum"}

I need to be able to save both as a json object in the same field, "data".

Is there a way I can do this?

Thanks in advance for any help, much appreciated :)

What might work for you is storing the data as a JValue rather than a String . You could use a JsonObjectField wrapping a case class that contains a JValue , which would allow an arbitrary structure, but it will give you an extra level of nesting in Mongo. To get around that, how about creating a custom field just to hold a JValue ?

One stab at it:

abstract class JValueObjectField[OwnerType <: BsonRecord[OwnerType]](rec: OwnerType)
   extends Field[JValue,OwnerType] 
    with MandatoryTypedField[JValue] 
    with MongoFieldFlavor[JValue] {

  def owner = rec

  def defaultValue = JNothing

  def toForm:  Box[NodeSeq] = Empty

  implicit val formats = owner.meta.formats

  def asJValue: JValue = valueBox openOr JNothing

  def setFromJValue(jv: JValue): Box[JValue] = Full(jv)

  def setFromString(in: String): Box[JValue] = tryo(JsonParser.parse(in)) match {
    case Full(jv: JValue) => setFromJValue(jv)
    case f: Failure => setBox(f)
    case other => setBox(Failure("Error parsing String into a JValue: "+in))
  }

  def setFromAny(in: Any): Box[JValue] = in match {
    case dbo: DBObject => setFromDBObject(dbo)
    case value: JValue => setBox(Full(value))
    case Some(value: JValue) => setBox(Full(value))
    case Full(value: JValue) => setBox(Full(value))
    case (value: JValue) :: _ => setBox(Full(value))
    case s: String => setFromString(s)
    case Some(s: String) => setFromString(s)
    case Full(s: String) => setFromString(s)
    case null|None|Empty => setBox(defaultValueBox)
    case f: Failure => setBox(f)
    case o => setFromString(o.toString)
  }

def asDBObject: DBObject = JObjectParser.parse(asJValue.asInstanceOf[JObject])

def setFromDBObject(dbo: DBObject): Box[JValue] =
  setFromJValue(JObjectParser.serialize(dbo))
}

...which looks a lot: all I've done is cut and paste from JValueObjectField with one parameter removed and fixed at JValue . There may be a smarter way to do that.

You can then use this in your model:

  object data extends JValueObjectField(this)

I'd populate it using the lift-json DSL:

 val json = ("name" -> "Bob") ~ ("result" -> 1)
 Recording.createRecord.data(json).save

This will give you something in your MongoDB document like this:

"data" : {
  "name" : "Bob",
  "result" : 1
}

BTW, the Lift Mailing list is a good way to get a better answer: that just happens to be where most of the Lift-related people seem to congregate.

class Recording private() extends MongoRecord[Recording] with ObjectIdPk[Recording] {

          def meta = Recording

          object data extends JObjectField(this)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM