2

The methods in ADSRegistrationMap are used to save and retrieve the document from MongoDB. ObjectId is created during initialization. I have to do the same to load Registration from Json that is part of POST body, so I thought I could just add ADSRegistrationProtocol object to do that. It fails with compilation error. Any idea on how to fix it or do this better?

package model

import spray.json._
import DefaultJsonProtocol._
import com.mongodb.casbah.Imports._
import org.bson.types.ObjectId
import com.mongodb.DBObject
import com.mongodb.casbah.commons.{MongoDBList, MongoDBObject}

case class Registration(
  system: String, 
  identity: String, 
  id: ObjectId = new ObjectId())

object RegistrationProtocol extends DefaultJsonProtocol {
  implicit val registrationFormat = jsonFormat2(Registration)
}

object RegistrationMap {
  def toBson(registration: Registration): DBObject = {
    MongoDBObject(
      "system"         -> registration.system,
      "identity"       -> registration.identity,
      "_id"            -> registration.id
    )
  }

  def fromBson(o: DBObject): Registration = {
    Registration(
      system = o.as[String]("system"),
      identity = o.as[String]("identity"),
      id = o.as[ObjectId]("_id")
    )
  }
}

Compilation Error:

[error] /model/Registration.scala:20: type mismatch;
[error]  found   : model.Registration.type
[error]  required: (?, ?) => ?
[error]  Note: implicit value registrationFormat is not applicable here because it comes after the application point and it lacks an explicit result type
[error]   implicit val registrationFormat = jsonFormat2(Registration)
[error]                                                    ^
[error] one error found
[error] (compile:compile) Compilation failed

Updated ObjectId to String and jsonFormat2 to jsonFormat3 to fix the compilation error.

case class Registration(
  system: String, 
  identity: String, 
  id: String = (new ObjectId()).toString())

object RegistrationProtocol extends DefaultJsonProtocol {
  implicit val registrationFormat = jsonFormat3(Registration)
}

Getting runtime error now when converting body of POST request to the Registration object. Any idea?

val route: Route = {
  pathPrefix("registrations") {
    pathEnd {
      post {
        entity(as[Registration]) { registration =>

Here is what is in build.sbt

scalaVersion := "2.10.4"

scalacOptions ++= Seq("-feature")

val akkaVersion = "2.3.8"

val sprayVersion = "1.3.1"

resolvers += "spray" at "http://repo.spray.io/"
resolvers += "Sonatype releases" at "https://oss.sonatype.org/content/repositories/releases"

// Main dependencies
libraryDependencies ++= Seq(
    "com.typesafe.akka" %% "akka-actor" % akkaVersion,
    "com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
    "com.typesafe.akka" %% "akka-camel" % akkaVersion,
    "io.spray" % "spray-can" % sprayVersion,
    "io.spray" % "spray-routing" % sprayVersion,
    "io.spray" % "spray-client" % sprayVersion,
    "io.spray" %% "spray-json" % sprayVersion,
    "com.typesafe" % "config" % "1.2.1",
    "org.apache.activemq" % "activemq-camel" % "5.8.0",
    "ch.qos.logback" % "logback-classic" % "1.1.2",
    "org.mongodb" %% "casbah" % "2.7.4"
)

Error:

12:33:03.477 [admcore-microservice-system-akka.actor.default-dispatcher-3] DEBUG s.can.server.HttpServerConnection - Dispatching POST request to http://localhost:8878/api/v1/adsregistrations to handler Actor[akka://admcore-microservice-system/system/IO-TCP/selectors/$a/1#-1156351415]
Uncaught error from thread [admcore-microservice-system-akka.actor.default-dispatcher-3] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[admcore-microservice-system]
java.lang.NoSuchMethodError: spray.json.JsonParser$.apply(Ljava/lang/String;)Lspray/json/JsValue;
    at spray.httpx.SprayJsonSupport$$anonfun$sprayJsonUnmarshaller$1.applyOrElse(SprayJsonSupport.scala:36)
    at spray.httpx.SprayJsonSupport$$anonfun$sprayJsonUnmarshaller$1.applyOrElse(SprayJsonSupport.scala:34)
Srini K
  • 3,315
  • 10
  • 37
  • 47
  • 1
    I think this is related to http://stackoverflow.com/questions/15156434/howto-test-custom-json-objects-with-spray-routing since `ObjectId` is not serializable by default. Also, you might need to change `jsonFormat2` to `jsonFormat3` – edi Jan 08 '15 at 14:08
  • Updated ObjectId to String and jsonFormat2 to jsonFormat3 to fix the compilation error. Getting a new runtime error now when converting the body of POST request to Registration instance. Any idea? – Srini K Jan 08 '15 at 17:48
  • 1
    Yes, I think you encountered a compatibility issue with spray-json, see [here](https://github.com/spray/spray/issues/932). Downgrade to `1.2.6` and things should work out. Alternatively you can also copy `SprayJsonSupport` as described in the ticket. – edi Jan 08 '15 at 18:12
  • Downgrading to 1.2.6 worked. Thanks. – Srini K Jan 08 '15 at 18:27

1 Answers1

0

To avoid any issue, I would define the Register class (which seems a data model) as follows

case class Register(system: String, identity: String, id: String)

That's because it makes more sense to me having an id field as String rather than as BSON ObjectId (I'm used to datamodel which don't depend on 3rd party libraries).

Therefore, the right SprayJson protocol would make use of jsonFormat3 rather than jsonFormat2:

object RegistrationProtocol extends DefaultJsonProtocol {
  implicit val registrationFormat = jsonFormat3(Registration)
}

And that would solve any kind of JSON serialization issue.

Finally, your toBson and fromBson converters would be:

def toBson(r: Registration): DBObject = {
    MongoDBObject(
      "system"    -> r.system,
      "identity"  -> r.identity,
      "_id"       -> new ObjectId(r.id)
    )
  }

and

def fromBson(o: DBObject): Registration = {
    Registration(
      system = o.as[String]("system"),
      identity = o.as[String]("identity"),
      id = o.as[ObjectId]("_id").toString
    )
  }

A that's where the BSON ObjectId is being used: much closer to the MongoDB dependant logic.

pangiole
  • 981
  • 9
  • 10