Search code examples
scalasprayreactivemongo

required: spray.httpx.marshalling.ToResponseMarshallable Error


Hey im pretty new to Spray and reactive mongo .

Im trying to return a list of result as json but i'm having some issue with converting the result to list of json.

this is my model

import reactivemongo.bson.BSONDocumentReader
import reactivemongo.bson.BSONObjectID
import reactivemongo.bson.Macros

case class Post(id: BSONObjectID, likes: Long, message: String, object_id: String, shares: Long)

object Post {
  implicit val reader: BSONDocumentReader[Post] = Macros.reader[Post]
}

the Mongo method

 def getAll(): Future[List[Post]] ={
 val query = BSONDocument(
  "likes" -> BSONDocument(
    "$gt" -> 27))
   collection.find(query).cursor[Post].collect[List]()
}

and this is the route

val route1 =
  path("posts") {
    val res: Future[List[Post]]= mongoService.getAll()
    onComplete(res) {
      case Success(value) => complete(value)
      case Failure(ex)    => complete(ex.getMessage)
    }
  }    

error

type mismatch; found : List[com.example.model.Post] required: spray.httpx.marshalling.ToResponseMarshallable

thanks,

miki


Solution

  • You'll need to define how a Post will be serialized, which you can do via a spray-json Protocol (see the docs for more detailed information). It's quite easy to do so, but before that, you'll also need to define a format for the BSONObjectId type, since there's no built-in support for that type in spray-json (alternatively, if object_id is a string representation of the BSONObjectId, think about removing the id property from your Post class or change it to be a String):

    // necessary imports
    import spray.json._
    import spray.httpx.SprayJsonSupport._
    
    implicit object BSONObjectIdProtocol extends RootJsonFormat[BSONObjectID] {
        override def write(obj: BSONObjectID): JsValue = JsString(obj.stringify)
        override def read(json: JsValue): BSONObjectID = json match {
          case JsString(id) => BSONObjectID.parse(id) match {
            case Success(validId) => validId
            case _ => deserializationError("Invalid BSON Object Id")
          }
          case _ => deserializationError("BSON Object Id expected")
        }
    }
    

    Now, we're able to define the actual protocol for the Post class:

    object PostJsonProtocol extends DefaultJsonProtocol {
        implicit val format = jsonFormat5(Post.apply)
    }
    

    Furthermore, we'll also need to make sure that we have the defined format in scope:

    import PostJsonProtocol._
    

    Now, everything will compile as expected.

    One more thing: have a look at the docs about the DSL structure of spray. Your mongoService.getAll() isn't within a complete block, which might not reflect your intentions. This ain't an issue yet, but probably will be if your route get more complex. To fix this issue simply put the future into the onComplete call or make it lazy:

    val route1 =
      path("posts") {
        onComplete(mongoService.getAll()) {
          case Success(value) => complete(value)
          case Failure(ex)    => complete(ex.getMessage)
        }
      }