play-json: mapping a json string to nested case class - scala

I have a usecase where incoming json string doesn't have a structure similar to the case class.
I have following case classes:
case class Parent(children: Seq[Child])
case class Child(name: Option[String], age: Option[Int])
User inputs in Parent field as:
John:20, Peter:12
Incoming json is as:
{"parent":{"parent":"John:20, Peter:12"},....}
Is it possible I write json formatter to map this to the Parent case class with nested Child list, using play framework json API?

Related

Same case class different validation

What I'm trying to do in Scala 2.11 and akka is have one case class but two different validations based on which route is being hit.
For example, let's consider the case class below
case class User(_id: String, name: String, age: Int, address: String)
Now while the /create route is hit, I don't need _id but I need all the other fields.
But while /update route is hit, I need the _id and the fields that are to be updated (which could be one or all three)
Only declaring Option doesn't serve the purpose because then my /create route goes for a toss.
Even extending case classes doesn't really work seamlessly (there's too much code duplicity).
I would love if something like this was possible
case class User(_id: String, name: String, age: Int, address: String)
case class SaveUser() extends User {
require(name.nonEmpty)
require(age.nonEmpty)
require(address.nonEmpty)
}
case class UpdateUser() extends User {
require(_id.nonEmpty)
}
Is there an elegant solution to this? Or do I have to create two identical case classes?
My suggestion would be to encode different case classes for different requirements, but if you insist you must share code between these two cases a possible solution would be to parameterize the case class
case class User[Id[_], Param[_]](_id: Id[String], name: Param[String], age: Param[Int], address: Param[String])
Then you define an alias for the Identity type constructor and your two uses of the case class
type Identity[T] = T
type SaveUser = User[Option, Identity]
type UpdateUser = User[Identity, Option]

How to create generic implicit conversions for json marshalling

I want to limit the construction of a case class to certain types and then be able to marshall that data back and forth.
For example, let's say I have a "home" case class that takes in a "kind" argument. I want to restrict the "kind" argument to a list of approved housing types, e.g., Apartment, Condo, etc.
object Home {
// this will create an implicit conversion to json object
implicit lazy val jsFormat = Jsonx.formatCaseClass[Home]
}
case class Home(owner: String, kind: HousingType)
What I need now is a way to marshall the various child types of HousingType. For example, here are some relationships:
trait HousingType
case object Apartment extends HousingType
case object Condo extends HousingType
Predictably, attempting to use this without specifying an implicit conversion yields the following error:
"could not find implicit value for parameter helper: ... HousingType"
Is there a way to create a generic implicit conversion for this?
You have to specify how your JSON marshaller has to transform your case object, as you have case class, it's quite simple for JSON marshaller to follow default behavior - take JSON field names from a case class and their type.
You need to indicate how to marshall/unmarshall case object directly, for instance via an implicit conversion.
implicit object HousingTypeMarshaller extends Writes[HousingType] {
def writes(housingType: HousingType) = housingType match {
case Apartment => Json.toJson("Apartment")
case Condo => Json.toJson("Condo")
}
}
p.s. I use usual play.json in this example because I didn't find any reason to use Jsonx, suggest you faced a limitation on 22 fields with Play Json, usual Play Json is suitable for this situation with case object.

Map JSON to case class for scalatest

I am trying to write test cases using scala test for the components.
My application takes JSON through the REST endpoints map it to the case class via Akka http entity mapping, Now while writing the test case all I want to do is Map my json to case class and utilize case class object without using the REST interface.
case class Sample(
projectName : String,
modelName: String,
field2 : String,
field3: FieldConf,
field4: String,
field5: String,
field6 : Seq[field7]
)
//FieldConf is another case class
how do I map my JSON string to this case class ?
When you configured your akka-http to unmarshal JSON to your case class, you had to configure some JSON library as the marshaller.
You can use the same library directly to parse and decode your case class.
For example, here's how you would do it using Circe:
import io.circe.parser.decode
decode[MyCaseClass]("{...}")

How does serialization work for case classes and compared to java?

When I have a case class that has methods on vals, what exactly will get serialized?
case class User(id: Int, name: String, age: Int) {
val someNumber = age * 10
def someMethod(a: Int) = ....
}
So from the above I would image the constructor parameters and the val someNumber would get serialized, while the methods would not.
So basically the state of the method gets serialized.
Are there any big differences between scala and java serialization?
case class serialization in Scala is what we would expect from standard Java serialization.
case classes extend scala.Serializable, which in turn extend Any with java.io.Serializable (see scala.Serializable), so case classes are 'marked' serializable using the usual Java method of extending java.io.Serializable.
Note that we should speak of object serialization, not class serialization. What gets serialized is the state of the object instance and it consists of the values of all members, declared or inherited. In the case of bodyless case class, like
case class User (id:Long, name:String)
all declared members will be serialized (eg. id, name).
If the case class declares inner member variables, those will be also included in the serialized form.
case class User (id:Long, name:String) {
val foo = name.hashCode * id
}
the serializad form will include (id, name, foo).
We can mark members with the #transient annotation to avoid them being serialized.
case class User (id:Long, name:String) {
val foo = name.hashCode * id
#transient bar = "Private Bar."
}
the serializad form will include (id, name, foo).
Note that Java serialization always involve serializing the object graph consisting of all references attached to the object being serialized, so any object that's referenced by a member variable will also be serialized.
Like in this case:
case class User (id:Long, name:String)
case class Product (id:Long, name:String, price: Decimal)
case class Purchase (timestamp:Long, user:User, product:Product, name:String)
so, given:
val beerPurchase = Purchase(now, onlineUser, leffe)
Serializing beerPurchase will also involve the serialization of the onlineUser and leffe objects.
Note that every member of a serializable class must be also serializable or be marked #transient. Otherwise, attempting to serialize such class will result in a runtime java.io.NotSerializableException
In a nutshell: No surprises. case class serialization is what you would expect from standard java/jvm serialization.
Let's create an object and serialize it so you can easily see what's being serialized:
case class City(name: String, funActivity: String, latitude: Double)
val bengaluru = City("Bengaluru", "South Indian food", 12.97)
implicit val cityRW = upickle.default.macroRW[City]
upickle.default.write(bengaluru) // "{\"name\":\"Bengaluru\",\"funActivity\":\"South Indian food\",\"latitude\":12.97}"
The values encapsulated in the object are being serialized.
You can write your serializer to persist data in the object. You can't serialize any JVM stuff/Scala stuff. Serializing means converting the object to a string / binary. Arrays of bytes have no way to store JVM stuff like methods. See this post for more info about serializing Scala objects.

Swagger models, dataType for case class definition

I have the following case class
case class AccountId(value: Long) extends EntityId(value)
And I use it in many DTOs, such as:
case class GetAccount(
#ApiModelProperty(dataType = "long")
id: AccountId,
email: Email
)
But in every dto in which I use AccountId I have to add ApiModelProperty to properly display the model structure in swagger.
Is it possible to annotate only the AccountIt case class definition and avoid the redundant code?