when I try to wrap my query in BSONDocument and putting my enumeratum enum as the value it docent compile.
for example, my enum:
sealed trait ProcessingStatus extends EnumEntry with UpperSnakecase
object ProcessingStatus extends Enum[ProcessingStatus] with ReactiveMongoBsonEnum[ProcessingStatus] {
val values: IndexedSeq[ProcessingStatus] = findValues
case object Processing extends ProcessingStatus
case object Done extends ProcessingStatus
}
and I have play json serializer that explains how to serialize:
object JsonSerialization {
import reactivemongo.api.bson._
implicit object ProcessingStatusReader extends BSONReader[ProcessingStatus] {
override def readTry(bson: BSONValue): Try[ProcessingStatus] = bson match {
case BSONString(s) => bson.asTry[ProcessingStatus]
case _ => Failure(new RuntimeException("String value expected"))
}
}
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
//Report Serializers
implicit val ProcessingStatusFormat: Format[ProcessingStatus] = EnumFormats.formats(ProcessingStatus)
implicit val ReportFormat: OFormat[Report] = Json.format[Report]
}
and now in my dao this does not compile:
import reactivemongo.play.json.compat.json2bson.{toDocumentReader, toDocumentWriter}
import serializers.JsonSerialization._
def findReport(reportId: String) = {
val test = BSONDocument("123" -> ProcessingStatus.Processing) // dosent compile
}
screenshot:
compilation error:
overloaded method apply with alternatives:
(elms: Iterable[(String, reactivemongo.api.bson.BSONValue)])reactivemongo.api.bson.BSONDocument <and>
(elms: reactivemongo.api.bson.ElementProducer*)reactivemongo.api.bson.BSONDocument
cannot be applied to ((String, enums.ProcessingStatus.Done.type))
val test = BSONDocument("status" -> ProcessingStatus.Done)
An IDE error is not a compilation error (recommend to use sbt and its console to tests).
Your code (simplified as bellow), is compiling fine, whatever is telling the IDE (which is wrong).
import reactivemongo.api.bson._
import scala.util.Try
trait ProcessingStatus {
def entryName = "foo"
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(new ProcessingStatus {})
Note.1: writeTry doesn't override anything, so the modifier is useless (and can lead to missunderstanding).
Note.2: Try(..) with a pure value such as BSONString(t.entryName) is over-engineered, rather use Success(..).
Note.3: Convenient factories are available such as val w = BSONWriter[T] { t => ... }.
Edit:
The typeclass BSONWriter (as most typeclass) is invariant, so having a BSONWriter[T] in the implicit scope doesn't allow to resolve a BSONWriter[U] forSome { U <: T }.
trait ProcessingStatus {
def entryName: String
}
object ProcessingStatus {
case object Done extends ProcessingStatus { val entryName = "done" }
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(ProcessingStatus.Done
/*
<console>:32: error: could not find implicit value for parameter writer: reactivemongo.api.bson.BSONWriter[ProcessingStatus.Done.type]
BSON.write(ProcessingStatus.Done)
*/
// --- BUT ---
BSON.write(ProcessingStatus.Done: ProcessingStatus)
// Success(BSONString(done))
Also exposing Done (and other cases) as ProcessingStatus in the API is working.
import reactivemongo.api.bson._
import scala.util.Try
sealed trait ProcessingStatus {
def entryName: String
}
object ProcessingStatus {
val Done: ProcessingStatus = new ProcessingStatus { val entryName = "done" }
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(ProcessingStatus.Done)
I have class as below
trait RiskCheckStatusCode {
def code: String
def isSuccess: Boolean
}
object RiskCheckStatusCode {
val SUCCESS = SuccessRiskCheckStatusCode("1.1.1")
val FAIL = FailRiskCheckStatusCode("2.2.2")
case class SuccessRiskCheckStatusCode(code: String) extends RiskCheckStatusCode {
override def isSuccess = true
}
object SuccessRiskCheckStatusCode {
import spray.json.DefaultJsonProtocol._
implicit val formatter = jsonFormat1(SuccessRiskCheckStatusCode.apply)
}
case class FailRiskCheckStatusCode(code: String) extends RiskCheckStatusCode {
override def isSuccess = false
}
object FailRiskCheckStatusCode {
import spray.json.DefaultJsonProtocol._
implicit val formatter = jsonFormat1(FailRiskCheckStatusCode.apply)
}
}
and now I would like to convert the list of RiskCheckStatusCode to json
object Main extends App{
import spray.json._
import spray.json.DefaultJsonProtocol._
val l = List(RiskCheckStatusCode.SUCCESS, RiskCheckStatusCode.FAIL)
implicit object RiskCheckStatusCodeJsonFormat extends JsonWriter[RiskCheckStatusCode] {
override def write(obj: RiskCheckStatusCode): JsValue = obj match {
case obj: SuccessRiskCheckStatusCode => obj.toJson
case obj: FailRiskCheckStatusCode => obj.toJson
}
}
def json[T](list: T)(implicit formatter: JsonWriter[T]) = {
print(list.toJson)
}
json(l)
}
but the json method can not find jsonWriter[RiskCheckStatusCode].
Can you explain why? Maybe should I do it differently for trait type?
Edit:
It works for
val l: RiskCheckStatusCode = RiskCheckStatusCode.SUCCESS
so the problem is with List[RiskCheckStatusCode] because I have a formatter for RiskCheckStatusCode, not for List[RiskCheckStatusCode]. I tried import DefaultJsonProtocol but it still does not work.
import spray.json.DefaultJsonProtocol._
I have to change the definitions? From
implicit object RiskCheckStatusCodeJsonFormat extends JsonWriter[RiskCheckStatusCode]
to
implicit object RiskCheckStatusCodeJsonFormat extends JsonWriter[List[RiskCheckStatusCode]]
error:
Error:(28, 7) Cannot find JsonWriter or JsonFormat type class for List[com.example.status.RiskCheckStatusCode]
json(l)
Error:(28, 7) not enough arguments for method json: (implicit formatter: spray.json.JsonWriter[List[com.example.status.RiskCheckStatusCode]])Unit.
Unspecified value parameter formatter.
json(l)
Your code is fine you are just not having toJson in your scope (it is located in the package object of spray.json).
Add it and your code should compile:
object Main extends App with DefaultJsonProtocol {
import spray.json._
// ...
}
Furthermore spray has some issues to lift JsonWriter through derived formats (see this for details).
You can switch to JsonFormat instead:
implicit object RiskCheckStatusCodeJsonFormat extends JsonFormat[RiskCheckStatusCode] {
override def write(obj: RiskCheckStatusCode): JsValue = obj match {
case obj: SuccessRiskCheckStatusCode => obj.toJson
case obj: FailRiskCheckStatusCode => obj.toJson
}
override def read(json: JsValue): RiskCheckStatusCode = ???
}
In addition, to cleanup the type of your List change the definition of RiskCheckStatusCode to (this explains more details):
sealed trait RiskCheckStatusCode extends Serializable with Product
I have the following test class for an actor:
class SomeActorSpec extends TestKit(ActorSystem("testSystem"))
with ImplicitSender
with WordSpecLike with MustMatchers {
it should "check the id of a submitted job" {
val tester = TestProbe()
val someActorRef = system.actorOf(Props(classOf[SomeActor]))
tester.send(someActorRef, SomeMessage(UUID.randomUUID))
tester.expectMsg(SomeReply("Not running"))
}
}
I'm getting this error:
type mismatch;
[error] found : your.package.SomeReply
[error] required: Int
[error] tester.expectMsg(SomeReply("Not running"))
Why would expectMsg require an Int?
I looked over different examples of using expectMsg and it was able to receive subtypes of the Message class.
Strange, it loads implicit from another scope. I suggest you to write (as in your earlier question) this way:
import java.util.UUID
import akka.actor.{Actor, ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import org.scalatest.FlatSpec
class SomeActorSpec extends FlatSpec {
it should "check the id of a submitted job" in new TestScope {
val tester = TestProbe()
tester.send(someActorRef, SomeMessage(UUID.randomUUID))
tester.expectMsg(SomeReply("Not running"))
}
abstract class TestScope extends TestKit(ActorSystem("testSystem")) {
val someActorRef = system.actorOf(Props(classOf[SomeActor]))
}
}
case class SomeMessage(v: UUID)
case class SomeReply(msg: String)
class SomeActor() extends Actor {
def receive = {
case msg => sender() ! SomeReply("Not running")
}
}
I need help with implementing a model of a notification using phantom and cassandra. What I have done till now:
import java.util.UUID
import com.websudos.phantom.dsl._
import com.websudos.phantom.connectors.Connector
import org.joda.time.DateTime
import scala.concurrent.Future
case class Notification(
id: UUID,
userId: UUID,
timestamp: DateTime,
read: Boolean,
actionUser: List[String],
verb: String,
itemId: UUID,
collectionId: String
)
sealed class NotificationTable extends CassandraTable[NotificationTable, Notification] {
object id extends UUIDColumn(this) with ClusteringOrder[UUID] with Ascending
object userId extends StringColumn(this) with PartitionKey[String]
object timestamp extends DateTimeColumn(this) with ClusteringOrder[DateTime] with Descending
object read extends BooleanColumn(this)
object actionUser extends ListColumn[NotificationTable, Notification, String](this)
object verb extends StringColumn(this)
object itemId extends UUIDColumn(this)
object collectionId extends StringColumn(this)
def fromRow(row: Row): Notification =
Notification(
id(row),
userId(row),
timestamp(row),
read(row),
actionUser(row),
verb(row),
itemId(row),
collectionId(row)
)
}
object NotificationTable extends NotificationTable with Connector {
override def keySpace: String = "test"
implicit val keyspace: com.websudos.phantom.connectors.KeySpace = com.websudos.phantom.connectors.KeySpace("test")
def insertItem(item: Notification): Future[ResultSet] =
insert
.value(_.id, item.id)
.value(_.userId, item.userId)
.value(_.timestamp, item.timestamp)
.value(_.read, item.read)
.value(_.actionUser, item.actionUser)
.value(_.verb, item.verb)
.value(_.itemId, item.itemId)
.value(_.collectionId, item.collectionId)
.future()
}
Somehow, I have to define two keyspaces, one for RootConnector and one for the insert statement. This is close enough to: this example,. Yet, my code does not compile. I know that they are using an abstract class there and hence it compiles.
My question is how would I go about using that abstract class?? I want to just call the insert statement from another scala source.
You are missing the fact that you are meant to use RootConnector instead of a random Connector trait there. The reason why that class is abstract is because it should only be instantiated inside of a Database object.
Have a look at this tutorial for more details, but in short, notice the RootConnector mixin here:
abstract class ConcreteNotificationTable extends
NotificationTable with RootConnector
And then:
class MyDatabase(val connector: KeySpaceDef) extends Database(connector) {
// And here you inject the real session and keyspace in the table
object notifications extends ConcreteNotificationsTable with connector.Connector
}
Then you do something like this:
object MyDatabase extends MyDatabase(ContactPoint.local.keySpace("my_app"))
And from every other source file:
val notification = Notification(id, //etc...)
MyDatabase.notifications.insertItem(someNotification)
And even better seperation of concern, as available in the tutorial:
trait DbProvider extends DatabaseProvider {
def database: MyDatabase
}
trait ProductionDbProvider extends DbProvider {
// this would now point to your object
override val database = MyDatabase
}
Then every single place which needs a database would need to mixin either DbProvider or directly ProductionDbProvider. Read the tutorial for more details, this is not a super trivial topic and all the details are already there.
Try:
import java.util.UUID
import com.websudos.phantom.dsl._
import com.websudos.phantom.connectors.Connector
import org.joda.time.DateTime
import scala.concurrent.Future
case class Notification(
id: UUID,
userId: UUID,
timestamp: DateTime,
read: Boolean,
actionUser: List[String],
verb: String,
itemId: UUID,
collectionId: String
)
//use normal class
class NotificationTable extends CassandraTable[NotificationTable, Notification] {
object id extends UUIDColumn(this) with ClusteringOrder[UUID] with Ascending
object userId extends StringColumn(this) with PartitionKey[String]
object timestamp extends DateTimeColumn(this) with ClusteringOrder[DateTime] with Descending
object read extends BooleanColumn(this)
object actionUser extends ListColumn[NotificationTable, Notification, String](this)
object verb extends StringColumn(this)
object itemId extends UUIDColumn(this)
object collectionId extends StringColumn(this)
def fromRow(row: Row): Notification =
Notification(
id(row),
userId(row),
timestamp(row),
read(row),
actionUser(row),
verb(row),
itemId(row),
collectionId(row)
)
}
//use abstract
abstract class NotificationTable extends NotificationTable with Connector {
def insertItem(item: Notification): Future[ResultSet] =
insert
.value(_.id, item.id)
.value(_.userId, item.userId)
.value(_.timestamp, item.timestamp)
.value(_.read, item.read)
.value(_.actionUser, item.actionUser)
.value(_.verb, item.verb)
.value(_.itemId, item.itemId)
.value(_.collectionId, item.collectionId)
.future()
}
Suppose I have an enumeration or sealed group of case objects as follows:
sealed abstract class Status
case object Complete extends Status
case object Failed extends Status
case object Pending extends Status
case object Unknown extends Status
or
object Status extends Enumeration {
val Complete, Failed, Pending, Unknown = Value
}
What is the easiest way to create json formats for these so that I can very easily (programmatically) generate json formats for use in a custom JsonFormat factory method, such as the following, which works for all normal case classes, strings, collections, etc., but produces {} or {"name": null} for the above two types of enumerations?:
import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods.parse
import org.json4s.jackson.Serialization
import org.json4s.jvalue2extractable
import org.json4s.string2JsonInput
trait JsonFormat[T] {
def read(json: String): T
def write(t: T): String
}
object JsonFormat {
implicit lazy val formats = DefaultFormats
def create[T <: AnyRef: Manifest](): JsonFormat[T] = new JsonFormat[T] {
def read(json: String): T = parse(json).extract[T]
def write(t: T): String = Serialization.write(t)
}
}
We've used org.json4s.ext.EnumNameSerializer to serialize enumerations:
import org.json4s._
import org.json4s.ext.EnumNameSerializer
class DoesSomething {
implicit lazy val formats = DefaultFormats + new EnumNameSerializer(Status)
...stuff requiring serialization or deserialization...
}
In practice we have mixin trait that adds the implicit format and defines all of our custom serializer/desrializers:
trait OurFormaters extends Json4sJacksonSupport {
implicit lazy val json4sJacksonFormats:Formats = DefaultFormats +
UuidSerializer +
new EnumNameSerializer(Status) +
...
}
object UuidSerializer extends CustomSerializer[UUID](format =>
(
{
case JString(s) => UUID.fromString(s)
case JNull => null
},
{
case x: UUID => JString(x.toString)
}
)
)