Play Framework, Format[A] vs OFormat[A] - scala

I can't find an explanation in the Play Framework docs.
I found this question but now I can't see why will I use Format[A] at all.

As Gaƫl mentioned:
OFormat will only read/write JSON objects, but maybe you have some types that you want to read/write as JSON string/int/Boolean.
OFormat/OWrite returns a JsObject while Format/Write returns JsValue.
You can do more with JsObject because it is a subclass of JsValue that represents a JSON object. JsValue can be a string, numeric, object or array.

Related

How to define an implicit deserializer using Play Json for generic types

I have a method accepting T and I'd like to do Json.parse(someString).as[T].
Now the T classes that I pass in have implicit formats defined such as implicit lazy val format: Format[Foo] = .... However, I'd like to be able to tell the compiler to find the implicit formats at runtime instead of complaining "No Json deserializer found for type T".
The correct way to do this is to add a context bound on T:
def yourMethod[T: Reads](...) = ...
It won't be looking for the implicits at runtime (which Scala can't do), but won't let you call the method if there is no implicit like format available in scope. And when there is, it'll just pass it through to as and any other methods which need it.
If your method needs to serialize as well as serialize, you'll need both bounds: T: Reads: Writes or just T: Format.

ReactiveMongo Macros.handler crashes after new field is added

I recently add a new field to my scala case class since I want to start keeping track of that field in my MongoDB.
let's say case class is like this:
case class MyItem (
var existingField: String,
var newlyAddedField: String
) {
}
I use this to serialize/deserialize json and bson to my object:
object MyItem {
import play.api.libs.json.Json
// generate reader and writer:
implicit var jsonFormats = Json.format[MyItem]
implicit var bsonFormats = Macros.handler[MyItem]
}
As all the existing data in my db doesn't have newlyAddedField, I get runtime exception
reactivemongo.bson.exceptions.DocumentKeyNotFound: The key 'newlyAddedField' could not be found in this document or array
Could anyone help? I've read about writing my own serialization/deserialization but I am not sure how to do that as I am using Play Framework whose syntax and way to do things are all different among its versions. I hope there is a simpler way as adding field should be common in NoSQL db. Btw, I am using play framework 2.5
Thanks in advance!
AFAIU it Macros.handler is null-safe i.e. it doesn't sets value to null if there is no field. I think the simplest and the cleanest fix this in Scala is to declare your new field as Option[String] so everyone (including the macro code-generator) will see that this field might be absent. And this seems to be what the doc suggests as well

No Json serializer as JsObject found for type play.api.libs.json.JsObject. Try to implement an implicit OWrites or OFormat for this type

I am newbie to mongodb and also to reactive mongo and I'm trying to integrate this example to my project https://github.com/sgodbillon/reactivemongo-demo-app
Firstly I would like to work only with class article but when I integrate the model and the controller I get this error:
No Json serializer as JsObject found for type play.api.libs.json.JsObject. Try to implement an implicit OWrites or OFormat for this type.
for this line in the controller:
63 // the cursor of documents
64 val found = collection.find(query).cursor[Article]
Have you implement writes for Article?
simple macros must helps you
implicit val articleWrite = Json.writes[Article]

When to use object and when to use class in Scala

I have a service object called MyService with functions defined that are used by my Play application's controllers. One particular function in MyService is parsing some text, and turning it into a JSON object. So my process will be:
Parse some text containing unstructured book info (title, author etc) into some Scala objects (Book objects)
Convert the Book objects into JSON format
Return the JSON
What I am wondering is, in the step where I parse the text and create my Scala objects, how should I define them? If this was Java I would just have an inner class named 'Book', but with Scala I don't know whether I should define an inner object or inner class inside my MyService object, and I don't know why/when I would choose one over the other.
Could someone explain when to use an object and when to use a class?
You use object when you only want EXACTLY ONE instance of your class.
objects can't have parameters and it's methods and values can be accessed via MyObject.myMethod.
In Java you would use the singleton pattern to achieve what object is in Scala.
Then you would have something like MyObject.getInstance().myMethod.
In your case you want to parse information into a class. I would make the parser class an object (assuming the parsing process is static).
The result however is not static, since it relies on the parsed data. Book should definitly be a class. (If there is exactly one Book, the parsing would not make much sense, would it?)
#Kigyo's answer is fine, but there's something else that should be addressed here. You're working within an MVC framework, and this Book you're describing sounds exactly like a model. This MyService object you describe is starting to sound like it's very bloated, and serializing a Book as JSON is something you can do entirely within a Play model.
package models
import play.api.libs.json._
case class Book(title: String, author: String, pages: Int)
object Book {
/** Use one of Play's json macros to define an implicit serializer/deserializer */
implicit val jsonFormat: Format[Book] = Json.format[Book]
def parse(rawText: String): Book = {
// Move your book parsing logic to here and construct the new Book instance.
}
}
Then in your controller function you would do something like:
import play.api.libs.json._
val rawText: String = ... // The raw text you're parsing the book data from.
val book: Book = Book.parse(rawText) // The parsed Book
val json: JsValue = Json.toJson(book) // The Book as a Play JSON object (can be returned as a JSON response)
val jsonString: String = Json.stringify(json) // The Book JSON as a String object, in case you need that..
This is a little more verbose than it needs to be, but I separated it out line-by-line to illustrate what's happening at each step. If all you wanted was the JSON as a string Jsons.stringify(Json.toJson(Book.parse(rawText))) would suffice.
Now the Book logic is self-contained, and not cluttering up another object.

Case classes for formatting json - with and without the object id

I am writing a play2 app that gets data via rest/json and stores it in mongodb using reactivemongo.
I am using a model built from case classes and implicit val myFormat = Json.format[myCaseClass]
Currently I have a case class for objects coming from mongodb. They contain the _id field and everything works. New objects coming in do naturally don't have this id field and so the Json.fromJson[myCaseClass](req.body) validator fails.
Do I really have to create another case class for new objects or is there a more DRY and elegant solution without duplicating the class and removing the _id?
I would use the parser combinator API and create a json format, or maybe even just a Reads[T], that handles incoming possibly id-less fields. Something like:
implicit val readsMyClass: Reads[MyClass] = (
(__ \ "id").readNullable[Id] and
(__ \ "someProperty").read[String]
)(create _)
def create(maybeId: Option[Id], someProperty: String) =
MyClass(maybeId.getOrElse(...generate id...), someProperty)
See the docs for more info: http://www.playframework.com/documentation/2.2.x/ScalaJsonCombinators
I followed the suggestions and _id: Option[BSONObjectID] does the trick.
It was not necessary to implement a reader because implicit val userFormat = Json.format[User] is able to create a macro containing the options.