I have the following class defined in Scala using Jackson as mapper.
package models
import play.api.Play.current
import org.codehaus.jackson.annotate.JsonProperty
import net.vz.mongodb.jackson.ObjectId
import play.modules.mongodb.jackson.MongoDB
import reflect.BeanProperty
import scala.collection.JavaConversions._
import net.vz.mongodb.jackson.Id
import org.codehaus.jackson.annotate.JsonIgnoreProperties
case class Team(
#BeanProperty #JsonProperty("teamName") var teamName: String,
#BeanProperty #JsonProperty("logo") var logo: String,
#BeanProperty #JsonProperty("location") var location: String,
#BeanProperty #JsonProperty("details") var details: String,
#BeanProperty #JsonProperty("formOfSport") var formOfSport: String)
object Team {
private lazy val db = MongoDB.collection("teams", classOf[Team], classOf[String])
def save(team: Team) { db.save(team) }
def getAll(): Iterable[Team] = {
val teams: Iterable[Team] = db.find()
return teams
}
def findOneByTeamName(teamName: String): Team = {
val team: Team = db.find().is("teamName", teamName).first
return team
}
}
Inserting into mongodb works without problems and an _id is automatically inserted for every document.
But now I want to try read (or deserialize) a document e.g. by calling findOneByTeamName. This always causes an UnrecognizedPropertyException for _id. I create the instance with Team.apply and Team.unapply. Even with an own ObjectId this doesn't work as _id and id are treated different.
Can anyone help how the get the instance or how to deserialize right? Thanks in advance
I am using play-mongojack. Here is my class. You object definition is fine.
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import org.mongojack.{MongoCollection, JacksonDBCollection}
import org.mongojack.ObjectId
import org.mongojack.WriteResult
import com.mongodb.BasicDBObject
import scala.reflect.BeanProperty
import javax.persistence.Id
import javax.persistence.Transient
import java.util.Date
import java.util.List
import java.lang.{ Long => JLong }
import play.mongojack.MongoDBModule
import play.mongojack.MongoDBPlugin
import scala.collection.JavaConversions._
class Event (
#BeanProperty #JsonProperty("clientMessageId") val clientMessageId: Option[String] = None,
#BeanProperty #JsonProperty("conversationId") val conversationId: String
) {
#ObjectId #Id #BeanProperty var messageId: String = _ // don't manual set messageId
#BeanProperty #JsonProperty("uploadedFile") var uploadedFile: Option[(String, String, JLong)] = None // the upload file(url,name,size)
#BeanProperty #JsonProperty("createdDate") var createdDate: Date = new Date()
#BeanProperty #Transient var cmd: Option[(String, String)] = None // the cmd(cmd,param)
def createdDateStr() = {
val format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
format.format(createdDate)
}
}
Related
I'm trying to use the Scala mongo driver with case classes, as described at: http://mongodb.github.io/mongo-scala-driver/2.2/getting-started/quick-tour-case-classes/
However I'm getting the exception:
Can't find a codec for class com.foo.model.User$.
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class com.foo.model.User$.
when I try to insert an item.
The case class:
case class User(
_id: ObjectId,
foo: String = "",
foo2: String = "",
foo3: String = "",
first: String,
last: String,
username: String,
pwHash: String = ""
gender: String,
isFoo: Boolean = false)
extends FooTrait
The code:
val providers = fromProviders( classOf[User])
val registry = fromRegistries(providers, DEFAULT_CODEC_REGISTRY)
val connStr = "mongodb://...."
val clusterSettings = ClusterSettings.builder().applyConnectionString(new ConnectionString(connStr)).build()
val clientSettings = MongoClientSettings.builder().codecRegistry(getCodecRegistry).clusterSettings(clusterSettings).build()
val client = MongoClient( clientSettings )
val database: MongoDatabase = client.getDatabase(dbName).withCodecRegistry(registry)
val modelCollection: MongoCollection[User] = db.getCollection("user")
val item = User(.....) //snipped
modelCollection.insertOne(item).toFuture()
Full stack trace:
Can't find a codec for class com.foo.model.User$.
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class com.foo.model.User$.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.async.client.MongoCollectionImpl.getCodec(MongoCollectionImpl.java:1170)
at com.mongodb.async.client.MongoCollectionImpl.getCodec(MongoCollectionImpl.java:1166)
at com.mongodb.async.client.MongoCollectionImpl.executeInsertOne(MongoCollectionImpl.java:519)
at com.mongodb.async.client.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:501)
at com.mongodb.async.client.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:496)
at org.mongodb.scala.MongoCollection.$anonfun$insertOne$1(MongoCollection.scala:410)
at org.mongodb.scala.MongoCollection.$anonfun$insertOne$1$adapted(MongoCollection.scala:410)
at org.mongodb.scala.internal.ObservableHelper$$anon$2.apply(ObservableHelper.scala:42)
at org.mongodb.scala.internal.ObservableHelper$$anon$2.apply(ObservableHelper.scala:40)
at com.mongodb.async.client.SingleResultCallbackSubscription.requestInitialData(SingleResultCallbackSubscription.java:38)
at com.mongodb.async.client.AbstractSubscription.tryRequestInitialData(AbstractSubscription.java:151)
at com.mongodb.async.client.AbstractSubscription.request(AbstractSubscription.java:82)
at org.mongodb.scala.ObservableImplicits$BoxedSubscription.request(ObservableImplicits.scala:474)
at org.mongodb.scala.ObservableImplicits$ScalaObservable$$anon$2.onSubscribe(ObservableImplicits.scala:373)
at org.mongodb.scala.ObservableImplicits$ToSingleObservable$$anon$3.onSubscribe(ObservableImplicits.scala:440)
at org.mongodb.scala.Observer.onSubscribe(Observer.scala:85)
at org.mongodb.scala.Observer.onSubscribe$(Observer.scala:85)
at org.mongodb.scala.ObservableImplicits$ToSingleObservable$$anon$3.onSubscribe(ObservableImplicits.scala:432)
at com.mongodb.async.client.SingleResultCallbackSubscription.<init>(SingleResultCallbackSubscription.java:33)
at com.mongodb.async.client.Observables$2.subscribe(Observables.java:76)
at org.mongodb.scala.ObservableImplicits$BoxedObservable.subscribe(ObservableImplicits.scala:458)
at org.mongodb.scala.ObservableImplicits$ToSingleObservable.subscribe(ObservableImplicits.scala:432)
at org.mongodb.scala.ObservableImplicits$ScalaObservable.headOption(ObservableImplicits.scala:365)
at org.mongodb.scala.ObservableImplicits$ScalaObservable.head(ObservableImplicits.scala:351)
at org.mongodb.scala.ObservableImplicits$ScalaSingleObservable.toFuture(ObservableImplicits.scala:410)
I think I'm doing everything right - and unless this is a bug, the code should work. My mongo-scala-driver version is 2.2.0.
Any ideas?
Here is a sample that is working on my local box with Mongo 3.6.1.
// ammonite script mongo.sc
import $ivy.`org.mongodb.scala::mongo-scala-driver:2.2.0`
import org.mongodb.scala._
import org.mongodb.scala.connection._
import org.mongodb.scala.bson.ObjectId
import org.mongodb.scala.bson.codecs.Macros._
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries.{fromRegistries, fromProviders}
trait FooTrait
case class User(_id: ObjectId,
foo: String = "",
foo2: String = "",
foo3: String = "",
first: String,
last: String,
username: String,
pwHash: String = "",
gender: String,
isFoo: Boolean = false) extends FooTrait
val codecRegistry = fromRegistries(fromProviders(classOf[User]), DEFAULT_CODEC_REGISTRY )
import scala.collection.JavaConverters._
val clusterSettings: ClusterSettings = ClusterSettings.builder().hosts(List(new ServerAddress("localhost")).asJava).build()
val settings: MongoClientSettings = MongoClientSettings.builder().clusterSettings(clusterSettings).build()
val mongoClient: MongoClient = MongoClient(settings)
val database: MongoDatabase = mongoClient.getDatabase("mydb").withCodecRegistry(codecRegistry)
val userCollection: MongoCollection[User] = database.getCollection("user")
val user:User = User(new ObjectId(), "foo", "foo2", "foo3", "first", "last", "username", "pwHash", "gender", true)
import scala.concurrent.duration._
import scala.concurrent.Await
// wait for Mongo to complete insert operation
Await.result(userCollection.insertOne(user).toFuture(),3.seconds)
When you save this snippet into a file mongo.sc, then you can run it with Ammonite using
amm mongo.sc
In case mongo is running on the default port, the mydb database should get created automatically including the new user collection.
I am new to Scala and Akka.
I have the following case class:
case class Demo(userId: String, date: java.util.Date, message: String) extends BusinessModel
I have to send List[Demo] in Json format as response to a get request but I am facing problem in the following code due to Date:
implicit val demoFormat: RootJsonFormat[Demo] = jsonFormat3(Demo)
I would be grateful if you may kindly help me out
You need to provide a format for java.util.Date, since spray doesn't have one by default.
A quick google search leads to https://gist.github.com/owainlewis/ba6e6ed3eb64fd5d83e7 :
import java.text._
import java.util._
import scala.util.Try
import spray.json._
object DateMarshalling {
implicit object DateFormat extends JsonFormat[Date] {
def write(date: Date) = JsString(dateToIsoString(date))
def read(json: JsValue) = json match {
case JsString(rawDate) =>
parseIsoDateString(rawDate)
.fold(deserializationError(s"Expected ISO Date format, got $rawDate"))(identity)
case error => deserializationError(s"Expected JsString, got $error")
}
}
private val localIsoDateFormatter = new ThreadLocal[SimpleDateFormat] {
override def initialValue() = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX")
}
private def dateToIsoString(date: Date) =
localIsoDateFormatter.get().format(date)
private def parseIsoDateString(date: String): Option[Date] =
Try{ localIsoDateFormatter.get().parse(date) }.toOption
}
Import DateMarshalling._ in piece code where you have wrote implicit val demoFormat: RootJsonFormat[Demo] = jsonFormat3(Demo) and it should be ok now :)
Is it possible to serialize object of below class using Json4s or lift or any other library?
class User(uId: Int) extends Serializable {
var id: Int = uId
var active: Boolean = false
var numTweets: Int = 0
var followers: ArrayBuffer[Int] = null
var following: ArrayBuffer[Int] = null
var userTimeline: Queue[String] = null
var homeTimeline: Queue[String] = null
var userTimelineSize: Int = 0
var homeTimelineSize: Int = 0
//var notifications: Queue[String] = null
var mentions: Queue[String] = null
var directMessages: Queue[String] = null
}
You can use Json4s for this purpose (with help of FieldSerializer), below is the code to get started with serialization of the User object:
def main(args: Array[String]) {
import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write, writePretty}
implicit val formats = DefaultFormats + FieldSerializer[User]()
val user = new User(12)
val json = write(user)
println(writePretty(user))
}
Also, in your non case class anything which is missing from the JSON needs to be an Option.
Another method would be to go for Genson:
def main(args: Array[String]) {
import com.owlike.genson._
import com.owlike.genson.ext.json4s._
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.JsonAST._
object CustomGenson {
val genson = new ScalaGenson(
new GensonBuilder()
.withBundle(ScalaBundle(), Json4SBundle())
.create()
)
}
// then just import it in the places you want to use this instance instead of the default one
import CustomGenson.genson._
val user = new User(12)
val jsonArray = toJson(user)
println(jsonArray)
}
I'm trying to prevent one of the properties of a Scala case class being serialised. I've tried annotating the property in question with the usual #JsonIgnore and I've also tried attaching the #JsonIgnoreProperties(Array("property_name")) to the case class. Neither of which seem to achieve what I want.
Here's a small example:
import org.json4s._
import org.json4s.jackson._
import org.json4s.jackson.Serialization
import org.json4s.jackson.Serialization.{read, write}
import com.fasterxml.jackson.annotation._
object Example extends App {
#JsonIgnoreProperties(Array("b"))
case class Message(a: String, #JsonIgnore b: String)
implicit val formats = Serialization.formats(NoTypeHints)
val jsonInput = """{ "a": "Hello", "b":"World!" }"""
val message = read[Message](jsonInput)
println("Read " + message) // "Read Message(Hello,World!)
val output = write(message)
println("Wrote " + output) // "Wrote {"a":"Hello","b":"World!"}"
}
Change your #JsonIgnore to #JsonProperty("b"). You have correctly stated to Ignore the property 'b but 'b has not yet been annotated as a property.
#JsonIgnoreProperties(Array("b"))
case class Message(a: String, #JsonProperty("b") b: String)
With jackson-databind 2.8.6 and jackson-module-scala 2.8.4
"com.fasterxml.jackson.core" % "jackson-databind" % "2.8.6",
"com.fasterxml.jackson.module" % "jackson-module-scala_2.11" % "2.8.4"
Only #JsonIgnoreProperties works fine,
Example case class as below where I'm ignoring "eventOffset" and "hashValue",
import java.util.Date
import com.fasterxml.jackson.annotation.{JsonIgnore, JsonIgnoreProperties}
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
#JsonIgnoreProperties(Array("eventOffset", "hashValue"))
case class TestHappenedEvent(eventOffset: Long, hashValue: Long, eventType: String,
createdDate: Date, testField: String) {
def this() {
this(0, 0, "", new Date(), "")
}
def toJSON(): String = {
val objectMapper = new ObjectMapper() with ScalaObjectMapper
objectMapper.registerModule(DefaultScalaModule)
val data = this.copy()
val stream = new ByteArrayOutputStream()
objectMapper.writeValue(stream, data)
stream.toString
}
}
test
import org.scalatest.FunSuite
import spray.json._
class BaseEventSpecs extends FunSuite {
val abstractEvent = TestHappenedEvent(0, 1, "TestHappenedEvent", new Date(2017, 10, 28), "item is sold")
test("converts itself to JSON") {
assert(abstractEvent.toJSON().parseJson ==
"""
{
"eventType":"TestHappenedEvent",
"createdDate":61470000000000,
"testField":"item is sold"
}
""".stripMargin.parseJson)
}
}
I have a case class that is made up of 2 embeded documents, one of which is a list. I am having some problems retriving the items in the list.
Please see my code below:
package models
import play.api.Play.current
import com.novus.salat._
import com.novus.salat.dao._
import com.mongodb.casbah.Imports._
import se.radley.plugin.salat._
import com.novus.salat.global._
case class Category(
_id: ObjectId = new ObjectId,
category: Categories,
subcategory: List[SubCategories]
)
case class Categories(
category_id: String,
category_name: String
)
case class SubCategories(
subcategory_id: String,
subcategory_name: String
)
object Category extends ModelCompanion[Category, ObjectId] {
val collection = mongoCollection("category")
val dao = new SalatDAO[Category, ObjectId](collection = collection) {}
val CategoryDAO = dao
def options: Seq[(String,String)] = {
find(MongoDBObject.empty).map(it => (it.category.category_id, it.category.category_name)).toSeq
}
def suboptions: Seq[(String,String,String)] = {
find(MongoDBObject.empty).map(it => (it.category.category_id, it.subcategory.subcategory_id, it.subcategory.subcategory_name)).toSeq
}
}
I get the error: value subcategory_id is not a member of List[models.SubCategories] which doesnt make any sense to me.
You are doing this:
def suboptions: Seq[(String,String,String)] = {
find(MongoDBObject.empty).map(category => {
val categories: Categories = category.category
val categoryId: String = categories.category._id
val subcategory: List[Subcategory] = category.subcategory
val subcategoryId: String = subcategory.subcategory_id //here you are trying to
//get id from list of subcategories, not one of them
val subcategoryName: String = subcategory.subcategory_name //same here
(categoryId, subcategoryId, subcategoryName)).toSeq
}
}
BTW. using snake_case in Scala is quite uncommon, val/var names should be in camelCase, see this
Edit: You can make it simple by doing this:
case class Category(
_id: ObjectId = new ObjectId(),
id: String,
name: String,
subcategories: List[Subcategory]
)
case class Subcategory(
id: String,
name: String
)
//not tested
def suboptions: Seq[(String, String, String)] = {
val categories = find(MongoDBObject.empty)
for{
category <- categories;
subcategory <- category.subcategories
} yield (category.id, subcategory.id, subcategory.name)
}