Mongo DB DAO and Spray JSON : - json

My Mongo DB abstract Dao is defined as follows
abstract class DbMongoDAO1[K, T <: Keyable[K]] (implicit val manifestT: Manifest[T], val manifestK: Manifest[K])
extends DbDAO[K, T]
with DbDAOExtensions[K, T]
with MongoConnection2
with JsonDbImplicits
{
val thisClass = manifestT.runtimeClass
val simpleName = thisClass.getSimpleName
lazy val collection = db.getCollection(s"${DbMongoDAO1.tablePrefix}$simpleName")
override def insertNew(r:T): Result[String,T] = {
val json: String = r.toJson.compactPrint
collection.insertOne(Document(json))
KO("Not Implemented")
}
}
I'm getting an error in the following line of code when converting a case class to JSON.
Error:(31, 26) value toJson is not a member of type parameter T
val json: String = r.toJson.compactPrint
val json: String = r.toJson.compactPrint
The trait JsonDbImplicits is as follows
trait JsonDbImplicits extends DefaultJsonProtocol
with SprayJsonSupport with JodaImplicits {
implicit val json_UserEmail:RootJsonFormat[UserEmail] = jsonFormat5(UserEmail)
implicit val json_UserProfile:RootJsonFormat[UserProfile] = jsonFormat13(UserProfile)
implicit val json_UserSession:RootJsonFormat[UserSession] = jsonFormat5(UserSession)
}
The case classes UserEmail and UserProfile are defined as follows
case class UserEmail
(
// it is the full email address
#Key("_id") id: String
, account_id: String
, active: Boolean = false
, ts_created: DateTime = now
, ts_updated: DateTime = now
) extends Keyable[String]
trait DbUserEmail extends DbMongoDAO1[String,UserEmail]
and
case class UserProfile
(
// id is the same as AccountId
#Key("_id") id: String = UUID.randomUUID().toString
, gender: Option[String] = None
, first_name: Option[String] = Some("")
, last_name: Option[String] = Some("")
, yob: Option[Int] = None
, kids: Option[Int] = None
, income: Option[Int] = None
, postcode: Option[String] = None
, location: Option[Boolean] = Some(true)
, opt_in: Option[Boolean] = Some(true)
, third_party: Option[Boolean] = Some(true)
, ts_created: DateTime = now
, ts_updated: DateTime = now
) extends Keyable[String]
trait DbUserProfile extends DbMongoDAO1[String,UserProfile]
What am I missing?

Related

Making JSON string to Kotlin object not working

I am trying to send a multipart/formdata object to the backend that contains a Freelancer object and two image files that have to be stored on the server. The saving on disc part works, but saving the JSON string as a freelancer object is not working. I tried converting the string with Jackson objectmapper but I think I am doing something wrong. When I debug the application it crashes at mapper.readValue and goes straight to the catch().
I also tried to work with kotlinx.serializer, but the import just would not work so I switched to Jackson.
The Kotlin controller that takes in the request:
private val imageDirectory = System.getProperty("user.dir") + "/images/"
#PostMapping(consumes = ["multipart/form-data"])
fun saveUser(
#RequestParam("profileImage") profileImage: MultipartFile,
#RequestParam("fileIdImage") fileIdImage: MultipartFile,
#RequestParam("freelancer") freelancer: String,
): ResponseEntity<*> {
return try {
val mapper = ObjectMapper();
val freelancerJson: Freelancer = mapper.readValue(freelancer, Freelancer::class.java)
println(freelancerJson.aboutYou)
makeDirectoryIfNotExist(imageDirectory)
val profileImagePath: Path = Paths.get(imageDirectory, profileImage.originalFilename)
val idImagePath: Path = Paths.get(imageDirectory, fileIdImage.originalFilename)
Files.write(profileImagePath, profileImage.bytes);
Files.write(idImagePath, fileIdImage.bytes);
JsonResponse(HttpStatus.OK, "Saved freelancer} ").createResponseEntity()
} catch (e: Exception) {
JsonResponse(HttpStatus.INTERNAL_SERVER_ERROR, e.message.toString()).createResponseEntity()
}
}
The request from the front end using vue:
The console output of the formdata:
Freelancer model:
#Entity
data class Freelancer(
#Id
val id: Int,
//maps ID to freelancer primary key
#MapsId
#OneToOne(targetEntity = User::class)
#JoinColumn(name = "freelancer_id")
//ignores the freelancer id because it is already mapped to val id
#JsonIgnore
val freelancerId: User,
val firstName: String? = null,
val lastName: String? = null,
val dateOfBirth: Date? = null,
val kvk: String? = null,
val btw: String? = null,
val phone: String? = null,
val street: String? = null,
val zipcode: String? = null,
val city: String? = null,
val housenumber: Int? = 0,
val addition: String? = null,
val nameCardHolder: String? = null,
val iban: String? = null,
val referral: String? = null,
val specialism: String? = null,
val aboutYou: String? = null,
val motivation: String? = null,
val workExperience: Int? = null,
val driverLicense: Boolean? = null,
val ownTransport: Boolean? = null,
val identificationImage: String? = null,
val smallBusinessScheme: Boolean? = false,
val profileImage: String? = null,
val previousWorkedPlatform: String? = null,
val servicesAmount: Int? = null,
val previousWorkedRating: Int? = null,
val foundBy: String? = null,
val privacyStatement: Boolean? = null,
)
I solved it by adding ID to the Json object I was sending to the backend, because my model has a one-to-one relation with another model I had to include it when mapping the Json to the model.

Scala Map Option

I have a map in scala like this.
val someData = Some(Map(genderKey -> gender,agekey -> age))
How to get the output as:
val key= genderkey
val value= gender
val key2 = agekey (Dynamic variable name)
val value2= age (Dynamic variable name)
Like this
someData.map(_.map {
case (k,v) => s"$k = $v"
}
.mkString(" and \n"))
.foreach(result => println(result))

Scala : How to do GroupBy sum for String values?

I have RDD[Row] :
|---itemId----|----Country-------|---Type----------|
| 11 | US | Movie |
| 11 | US | TV |
| 101 | France | Movie |
How to do GroupBy itemId so that I can save the result as List of json where each row is separate json object(each row in RDD) :
{"itemId" : 11,
"Country": {"US" :2 },"Type": {"Movie" :1 , "TV" : 1} },
{"itemId" : 101,
"Country": {"France" :1 },"Type": {"Movie" :1} }
RDD :
I tried :
import com.mapping.data.model.MappingUtils
import com.mapping.data.model.CountryInfo
val mappingPath = "s3://.../"
val input = sc.textFile(mappingPath)
The input is list of jsons where each line is json which I am mapping to the POJO class CountryInfo using MappingUtils which takes care of JSON parsing and conversion:
val MappingsList = input.map(x=> {
val countryInfo = MappingUtils.getCountryInfoString(x);
(countryInfo.getItemId(), countryInfo)
}).collectAsMap
MappingsList: scala.collection.Map[String,com.mapping.data.model.CountryInfo]
def showCountryInfo(x: Option[CountryInfo]) = x match {
case Some(s) => s
}
val events = sqlContext.sql( "select itemId EventList")
val itemList = events.map(row => {
val itemId = row.getAs[String](1);
val çountryInfo = showTitleInfo(MappingsList.get(itemId));
val country = if (countryInfo.getCountry() == 'unknown)' "US" else countryInfo.getCountry()
val type = countryInfo.getType()
Row(itemId, country, type)
})
Can some one let me know how can I achieve this ?
Thank You!
I can't afford the extra time to complete this, but can give you a start.
The idea is that you aggregate the RDD[Row] down into a single Map that represents your JSON structure. Aggregation is a fold that requires two function parameters:
seqOp How to fold a collection of elements into the target type
combOp How to merge two of the target types.
The tricky part comes in combOp while merging, as you need to accumulate the counts of values seen in the seqOp. I have left this as an exercise, as I have a plane to catch! Hopefully someone else can fill in the gaps if you have trouble.
case class Row(id: Int, country: String, tpe: String)
def foo: Unit = {
val rows: RDD[Row] = ???
def seqOp(acc: Map[Int, (Map[String, Int], Map[String, Int])], r: Row) = {
acc.get(r.id) match {
case None => acc.updated(r.id, (Map(r.country, 1), Map(r.tpe, 1)))
case Some((countries, types)) =>
val countries_ = countries.updated(r.country, countries.getOrElse(r.country, 0) + 1)
val types_ = types.updated(r.tpe, types.getOrElse(r.tpe, 0) + 1)
acc.updated(r.id, (countries_, types_))
}
}
val z = Map.empty[Int, (Map[String, Int], Map[String, Int])]
def combOp(l: Map[Int, (Map[String, Int], Map[String, Int])], r: Map[Int, (Map[String, Int], Map[String, Int])]) = {
l.foldLeft(z) { case (acc, (id, (countries, types))) =>
r.get(id) match {
case None => acc.updated(id, (countries, types))
case Some(otherCountries, otherTypes) =>
// todo - continue by merging countries with otherCountries
// and types with otherTypes, then update acc
}
}
}
val summaryMap = rows.aggregate(z) { seqOp, combOp }

Adding information from one JsonBuilder object to another

As the title suggests, I'm trying to add information held in one JsonBuilder object to a second JsonBuilder object.
Currently I have this:
public String buildOneUser(DyveUserDTO user)
{
def userBuilder = new JsonBuilder()
userBuilder user.collect { usr ->
[
'Name': usr.userName,
'Allowance': usr.allowance,
'Total Holidays in Calendar': usr.totalHolidaysInCal,
'Holidays Booked': usr.numHolidaysBooked,
'Holidays Taken': usr.numHolidaysTaken,
'Holidays Remaining': usr.totalHolidaysLeft
]
}
def userHolidayBuilder = new JsonBuilder()
userHolidayBuilder user.holidayEvents.collect { usr ->
[
'Start Date': usr.startDate,
'End Date': usr.endDate,
'Days': usr.days
]
}
def userAndHolidays = userBuilder + userHolidayBuilder
return userAndHolidays.toPrettyString()
}
user.holidayEvents is a list of objects representing holidays and it could be empty or have any number of objects in it. This made me hesitant of doing something like:
def userBuilder = new JsonBuilder()
userBuilder user.collect { usr ->
[
'Name': usr.userName,
'Allowance': usr.allowance,
'Total Holidays in Calendar': usr.totalHolidaysInCal,
'Holidays Booked': usr.numHolidaysBooked,
'Holidays Taken': usr.numHolidaysTaken,
'Holidays Remaining': usr.totalHolidaysLeft
'Holiday': usr.holidayEvents[0].startDate
'Holiday': usr.holidayEvents[0].endDate
'Holiday': usr.holidayEvents[0].days
]
}
As I would only get the amount of holidays I write code for. It would also throw an exception if a user had no holidays and I told it look at usr.holidayEvents[1] as it's outside of the list range.
I've also tried nesting a .collect like this
def userBuilder = new JsonBuilder()
userBuilder {
'Name' user.userName,
'Allowance' user.allowance,
'Total Holidays in Calendar' user.totalHolidaysInCal,
'Holidays Booked' user.numHolidaysBooked,
'Holidays Taken' user.numHolidaysTaken,
'Holidays Remaining' user.totalHolidaysLeft,
'Holidays' user.holidayEvents.collect{ evt ->
[
'Start Date': evt.startDate,
'End Date': evt.endDate,
'Days': evt.days
]
}
}
But this returned all the keys except the Holidays key.
Any help would be greatly appreciated!
EDIT - My code now looks like this:
public String buildOneUser(DyveUserDTO user)
{
def userBuilder = new JsonBuilder()
userBuilder user.collect { usr ->
[
'Name': usr.userName,
'Allowance': usr.allowance,
'Total Holidays in Calendar': usr.totalHolidaysInCal,
'Holidays Booked': usr.numHolidaysBooked,
'Holidays Taken': usr.numHolidaysTaken,
'Holidays Remaining': usr.totalHolidaysLeft,
'Holidays': usr.holidayEvents.collect{ evt ->
[
'Start Date': evt.startDate,
'End Date': evt.endDate,
'Days': evt.days
]
}
]
}
}
EDIT 2 - Sample Code
Method to call:
public String buildOneUser(DyveUserDTO user)
{
def userBuilder = new JsonBuilder()
userBuilder {
Name:
user.userName
Allowance:
user.allowance
TotalHolidaysInCalendar:
user.totalHolidaysInCal
HolidaysBooked:
user.numHolidaysBooked
HolidaysTaken:
user.numHolidaysTaken
HolidaysRemaining:
user.totalHolidaysLeft
Holidays:
user.holidayEvents.collect { evt ->
[
'Start Date': evt.startDate,
'End Date' : evt.endDate,
'Days' : evt.days
]
}
}
return userBuilder.toPrettyString()
}
User to pass in:
class DyveUserDTO
{
String firstName = "Foo"
String userName = "FooBar"
Integer userID = 42
BigDecimal numHolidaysBooked = 3
BigDecimal numHolidaysTaken = 0
BigDecimal totalHolidaysInCal = 3
BigDecimal totalHolidaysLeft = 12
BigDecimal allowance = 12
List<HolidayObject> holidayEvents = []
}
Holiday objects to go in holidayEvents:
class HolidayObject
{
public Integer userID = 42
public String title = "Foo Holiday"
public String event = "Holiday"
public String amPm = "Full Day"
public String name = "Foo"
public LocalDateTime startDate = LocalDateTime.parse(2015-02-20T00:00:00)
public LocalDateTime endDate = LocalDateTime.parse(2015-02-20T00:00:00)
public BigDecimal days = 1
}
class HolidayObject
{
public Integer userID = 42
public String title = "Foo Holiday Pm"
public String event = "Holiday"
public String amPm = "Pm"
public String name = "Foo"
public LocalDateTime startDate = LocalDateTime.parse(2015-02-23T00:00:00)
public LocalDateTime endDate = LocalDateTime.parse(2015-02-24T00:00:00)
public BigDecimal days = 2
}
each just returns the list it's called upon, collect should be used for events. See the working code below:
import groovy.json.JsonBuilder
class UserEvent {
def start
def end
def days
}
class User {
def name
def events
}
def u1 = new User(name: 'u1', events: [new UserEvent(start: 0, end: 1, days: 1), new UserEvent(start: 0, end: 2, days: 2)])
def u2 = new User(name: 'u2', events: [new UserEvent(start: 0, end: 3, days: 3)])
def users = [u1, u2]
def userBuilder = new JsonBuilder()
userBuilder users.collect { usr ->
[
'name': usr.name,
'events': usr.events.collect { e ->
[
start: e.start,
end: e.end,
days: e.days,
]
}
]
}
print userBuilder.toPrettyString()
EDIT
Below is a working example:
import groovy.json.JsonBuilder
user = new DyveUserDTO()
def userBuilder = new JsonBuilder()
userBuilder {
Name user.userName
Allowance user.allowance
TotalHolidaysInCalendar user.totalHolidaysInCal
HolidaysBooked user.numHolidaysBooked
HolidaysTaken user.numHolidaysTaken
HolidaysRemaining user.totalHolidaysLeft
Holidays user.holidayEvents.collect { evt ->
[
'Start Date': evt.startDate,
'End Date' : evt.endDate,
'Days' : evt.days
]
}
}
println userBuilder.toPrettyString()
class DyveUserDTO {
String firstName = "Foo"
String userName = "FooBar"
Integer userID = 42
BigDecimal numHolidaysBooked = 3
BigDecimal numHolidaysTaken = 0
BigDecimal totalHolidaysInCal = 3
BigDecimal totalHolidaysLeft = 12
BigDecimal allowance = 12
List<HolidayObject> holidayEvents = [new HolidayObject(), new HolidayObject()]
}
class HolidayObject {
public Integer userID = 42
public String title = "Foo Holiday"
public String event = "Holiday"
public String amPm = "Full Day"
public String name = "Foo"
public String startDate = '2015-02-20T00:00:00'
public String endDate = '2015-02-20T00:00:00'
public BigDecimal days = 1
}
No colons : needed. See the sample here. Also I have no Joda dependency so replaced with String.

Play 2.1 Json serialization of traits

The experimental "Inception" feature in Play 2.1 (Json.format[...]) only works for case classes (see here). How can I write my custom format implicit for a trait. I have the following construct:
sealed trait Plan {
def id: String
def name: String
def apps: Int
def users: Int
def testruns: Int
def price: Int
def prio: Int
}
And the following case classes which are extending the trait Plan.
case class Start(
id: String = "start",
name: String = "Start",
apps: Int = 1,
users: Int = 1,
testruns: Int = 10,
price: Int = 99,
prio: Int = 30) extends Plan
case class Pro(
id: String = "pro",
name: String = "Pro",
apps: Int = 2,
users: Int = 5,
testruns: Int = 25,
price: Int = 299,
prio: Int = 20) extends Plan
case class Premium(
id: String = "premium",
name: String = "Premium",
apps: Int = -1,
users: Int = -1,
testruns: Int = -1,
price: Int = 799,
prio: Int = 10) extends Plan
Now I need to write my custom implicit format val in the Plan companion object. I tried:
object Plan {
implicit val planFormats = (
(__ \ "id").format[String] and
(__ \ "name").format[String] and
(__ \ "apps").format[Int] and
(__ \ "users").format[Int] and
(__ \ "testruns").format[Int] and
(__ \ "price").format[Int] and
(__ \ "prio").format[Int]
)(Plan.apply, unlift(Plan.unapply))
}
However, a trait hasn't an apply or unapply method. What is the correct way to provide an implicit val for the json serialization in Play 2.1?
You simply have to provide your own function that creates a new instance from the given values.
Essentially a companion object to the trait that acts as a factory.
object Plan {
def apply(id: String, name: String, ...) = id match {
case "pro" => new Pro(id, name, ...)
...
}
def unapply(p: Person): Option[(String, String, ...)] = ...
}
Why are you using Traits and implementing case class ?
Why not using instances of classes, such as:
case class Plan (
id: String,
name: String,
apps: Int,
users: Int,
testruns: Int,
price: Int,
prio: Int
)
val start = new Plan("start", "Start", 1, 1, 10, 99, 30)
val pro = new Plan("pro", "Pro", 2, 5, 25, 299, 20)
val premium = new Plan("premium", "Premium", -1, -1, -1, 799, 10)
and then, you can keep your Json formatter:
object Plan {
implicit val planFormats = (
(__ \ "id").format[String] and
(__ \ "name").format[String] and
(__ \ "apps").format[Int] and
(__ \ "users").format[Int] and
(__ \ "testruns").format[Int] and
(__ \ "price").format[Int] and
(__ \ "prio").format[Int]
)(Plan.apply, unlift(Plan.unapply))
}