JSON Rendering Scala/Play 2.1 - scala

I am working on a small project try to get a Scala/Play backend working. I am trying to have it return and also process JSON on the web service side. I cannot seem to figure out how to get the JSON marshalling and unmarshalling to work. Could someone help me with this issue? I am using Play 2.1 and Scala 2.10. The error that I get is
"overriding method reads in trait Reads of type (json: play.api.libs.json.JsValue)play.api.libs.json.JsResult[models.Address]; method reads has incompatible type"
Edited. Someone else gave me the solution. For read you must use JsSuccess, not JsResult.
case class Address(id: Long, name: String)
object Address {
implicit object AddressFormat extends Format[Address] {
def reads(json: JsValue):Address = JsSuccess(Address(
(json \ "id").as[Long],
(json \ "name").as[String]
))
def writes(address: Address): JsValue = JsObject(Seq(
"id" -> JsNumber(address.id),
"name" -> JsString(address.name)
))
}
}

With Play 2.1 you could simplify your code:
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val addressFormat = (
(__ \ "id").format[String] and
(__ \ "name").format[Long]
)(Address.apply, unlift(Address.unapply))
More detailed information can be found here: ScalaJsonCombinators

You can simplify your code even further by using macros, although they are marked as experimental:
import play.api.libs.json._
import play.api.libs.functional.syntax._
case class Address(id: Long, name: String)
implicit val addressFormat = Json.format[Address]
More details on this technique in the official Play documentation.

Hey my solution would be:
import play.api.libs.json.JsonNaming.SnakeCase
import play.api.libs.json._
object Test {
implicit val config = JsonConfiguration(SnakeCase)
implicit val userFormat: OFormat[Test] = Json.format[Test]
}
case class Test(
testName: String,
testWert: String,
testHaus: String
)
In conclusion, you get a compenion object. The config converts all keys of the case class into snakecase. The implicit values ensure that a valid Json can be parsed into a model. So you get your test model back.
The Json should look like this:
{
"test_name" : "Hello",
"test_wert": "Hello",
"test_haus": "Hello"
}
https://www.playframework.com/documentation/2.6.x/ScalaJsonAutomated

Related

How to ignore a field from serializing when using circe in scala

I am using circe in scala and have a following requirement :
Let's say I have some class like below and I want to avoid password field from being serialised then is there any way to let circe know that it should not serialise password field?
In other libraries we have annotations like #transient which prevent field from being serialised ,is there any such annotation in circe?
case class Employee(
name: String,
password: String)
You could make a custom encoder that redacts some fields:
implicit val encodeEmployee: Encoder[Employee] = new Encoder[Employee] {
final def apply(a: Employee): Json = Json.obj(
("name", Json.fromString(a.name)),
("password", Json.fromString("[REDACTED]")),
)
}
LATER UPDATE
In order to avoid going through all fields contramap from a semiauto/auto decoder:
import io.circe.generic.semiauto._
implicit val encodeEmployee: Encoder[Employee] =
deriveEncoder[Employee]
.contramap[Employee](unredacted => unredacted.copy(password = "[REDACTED]"))
Although #gatear's answer is useful, it doesn't actually answer the question.
Unfortunately Circe (at least until version 0.14.2) does not have annotations to ignore fields. So far there is only a single annotation (#JsonKey) and this is used to rename field names.
In order to ignore a field when serialising (which Circe calls encoding) you can avoid that field in the Encoder implementation.
So instead of including the password field:
implicit val employeeEncoder: Encoder[Employee] =
Encoder.forProduct2("name", "password")(employee => (employee.name, employee.password))
you ommit it:
implicit val employeeEncoder: Encoder[Employee] =
Encoder.forProduct1("name")(employee => (u.name))
Alternatively what I've been using is creating a smaller case class which only includes the fields I'm interested in. Then I let Circe's automatic derivation kick in with io.circe.generic.auto._:
import io.circe.generic.auto._
import io.circe.syntax._
case class EmployeeToEncode(name: String)
// Then given an employee object:
EmployeeToEncode(employee.name).asJson
deriveEncoder.mapJsonObject(_.remove("password"))

spray-json in Http().bindAndHandle

I'm currently going through the akka-http documentation & examples. I'm stuck on probably something very trivial: passing in route-configuration with Directives and JsonSupport. I'm following the docs on introduction and json-support
For the JsonSupport I need to make a class extending akka.http.scaladsl.server.Directives:
class RouteDef extends Directives with JsonSupport {
with JsonSupport defined by me:
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
How can I now use this class in:
val bindingFuture = Http().bindAndHandle(new RouteDef().route, "localhost", 8082)
The json marshalling is not working, as the jsonsupport is not wired on the route val (I suspect).
A separate question: What is the status of spray-json? Spray is not supported anymore, will spray-json still be maintained or replace by Jackson for example?
I tried the example on the json-support page, and it works as expected.
// domain model
final case class Item(name: String, id: Long)
final case class Order(items: List[Item])
// collect your json format instances into a support trait:
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
implicit val itemFormat = jsonFormat2(Item)
implicit val orderFormat = jsonFormat1(Order) // contains List[Item]
}
// use it wherever json (un)marshalling is needed
class MyJsonService extends Directives with JsonSupport {
val route =
get {
pathSingleSlash {
complete(Item("thing", 42)) // will render as JSON
}
} ~
post {
entity(as[Order]) { order => // will unmarshal JSON to Order
val itemsCount = order.items.size
val itemNames = order.items.map(_.name).mkString(", ")
complete(s"Ordered $itemsCount items: $itemNames")
}
}
}
object Main extends App {
implicit val system = ActorSystem("main")
implicit val materializer = ActorMaterializer()
Http().bindAndHandle(new MyJsonService().route, "localhost", 8080)
}
And the result is :
~❯ curl http://127.0.0.1:8080/
{"name":"thing","id":42}%
~❯ curl -H "Content-Type: application/json" -X POST -d '{"items":[{"name":"thing2","id":43}]}' http://localhost:8080
Ordered 1 items: thing2%
So it should work unless you missed something like implicitly define system or materializer. In routing-dsl overview it described as follow:
The conversion from Route to flow can either be invoked explicitly using Route.handlerFlow or, otherwise, the conversion is also provided implicitly by RouteResult.route2HandlerFlow.
If this is the problem, maybe you should check that doc as well.
For spray-json, I don't know if it will be maintained. But as it's a lightweight JSON implementation and quite stable now, it's not likely to have a big change in the future.
And ofcoures, if you want to use a Jackson marshaler, it's not so difficult to create your own, like this.

http4s - get request body as String or InputStream

I'm trying to define HttpService that receives json and parses it to case class with json4s library:
import org.http4s._
import org.http4s.dsl._
import org.json4s._
import org.json4s.native.JsonMethods._
case class Request(firstName: String, secondName: String)
HttpService {
case req # POST -> Root =>
val request = parse(<map req.body or req.bodyAsText to JsonInput>).extract[Request]
Ok()
}
How can I get org.json4s.JsonInput from req.body or req.bodyAsText?
I know that json4s also have StringInput and StreamInput that inherits from JsonInput for using with String and InputStream so I think that I need to convert req.body to InputStream or req.bodyAsText to String but I still do not understand how.
I'm new to Scala and I do not yet fully understand some concepts such as scalaz.stream.Process.
You can use the http4s-json4s-jackson (or http4s-json4s-native) packages and use an org.http4s.EntityDecoder to easily get a Foo (I renamed your Request case class to Foo below) from a request.
EntityDecoder is a type class which can decode an entity from the request body.
We want to get the Foo posted in JSON, so we need to create an EntityDecoder[Foo] which can decode JSON. If we want to create this decoder using json4s we need a Reader (or a JsonFormat).
If you have an EntityDecoder[Foo] instance, we can get the Foo from the request with req.as[Foo].
import org.json4s._
import org.json4s.jackson.JsonMethods._
import org.http4s._
import org.http4s.dsl._
import org.http4s.json4s.jackson._
case class Foo(firstName: String, secondName: String)
// create a json4s Reader[Foo]
implicit val formats = DefaultFormats
implicit val fooReader = new Reader[Foo] {
def read(value: JValue): Foo = value.extract[Foo]
}
// create a http4s EntityDecoder[Foo] (which uses the Reader)
implicit val fooDec = jsonOf[Foo]
val service = HttpService {
case req # POST -> Root =>
// req.as[Foo] gives us a Task[Foo]
// and since Ok(...) gives a Task[Response] we need to use flatMap
req.as[Foo] flatMap ( foo => Ok(foo.firstName + " " + foo.secondName) )
}
Note: The json libraries libraries used most often with http4s are probably argonaut and circe. So you might find more http4s examples using one of those libraries.
Peter's solution both corrects the question and answers it, but I stumbled here looking for the solution to OP's stated, but not intended, question: "how to get request body as [...] InputStream" in http4s. Thanks to the discussion in Issue 634 on GitHub, here's what I came up with:
import java.io.InputStream
import org.http4s._
implicit val inputStreamDecoder: EntityDecoder[InputStream] =
EntityDecoder.decodeBy(MediaRange.`*/*`) { msg =>
DecodeResult.success(scalaz.stream.io.toInputStream(msg.body))
}
And then in your HttpService, use that decoder like so:
request.as[InputStream].flatMap { inputStream => ...inputStream is an InputStream... }
Or skip the whole Decoder dance, if you want:
val inputStream = scalaz.stream.io.toInputStream(request.body)
You may use flatMap and as inside it before calling the Http4s service to decode responses from it:
#Test def `Get json gives valid contact`: Unit = {
val request = Request[IO](GET, uri"/contact")
val io = Main.getJsonWithContact.orNotFound.run(request)
// here is magic
val response = io.flatMap(_.as[Json]).unsafeRunSync()
val contact = contactEncoder(Contact(1, "Denis", "123")) // this is encoding to json for assertion
assertEquals(contact, response)
}
This is how types work here:
val io: IO[Response[IO]] = Main.getJsonWithContact.orNotFound.run(request)
val response: IO[Json] = io.flatMap(_.as[Json])
val res: Json = response.unsafeRunSync()
as[String] will return the string just like this.

Force Play to JSON serialize timestamps as strings, not integer seconds

I'm trying to figure out how to get Play's toJSon method to serialize java.sql.Timestamp/java.sql.Date objects as date/time strings rather than seconds since epoch, which seems to be the default. I've tried two methods to accomplish this:
1) I changed the Jackson JSON configuration default as seen here in the Global onStart handler:
import play.api._
import play.libs.Json
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
object Global extends GlobalSettings {
override def onStart(app: play.api.Application){
println("really started")
var om = new ObjectMapper()
om.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
Json.setObjectMapper(om)
}
}
But this doesn't seem to have any effect. I can tell the code is executing based on the println statement but the serialization is unaffected.
2) Write a custom Writer for the java.sql.Date object:
implicit val sqlDateWrites: Writes[java.sql.Date] = new Writes[java.sql.Date] {
def writes(d: java.sql.Date): JsValue = JsString("WTF")
}
However this doesn't work either. I'm not sure if it's an error in how I'm writing it, or if I am just including it in the wrong place (I'm declaring it in the same file that I'm calling "toJson" in.
Any help would be appreciated.
https://gist.github.com/fancellu/f4b72e853766acf26bf16a7fb37cb8ac
Give this code a go, stores as ISO-8601 format
You're mixing up Play's Java JSON library and its Scala library.
If you're using Scala, only use play.api.libs.json. If you're in Java, play.libs.Json.
To create a Writes[java.sql.Date], call Writes.sqlDateWrites(pattern) with whatever pattern you're using.
val sqlDateWrite = Writes.sqlDateWrites(myPattern)
Then, when you create your Writes for whatever object you're converting:
case class Foo(id: Long, createdAt: java.sql.Date)
implicit val fooWrites: Writes[Foo] = (
(__ \ "id").write[Long] and
(__ \ "createdAt").write[java.sql.Date](sqlDateWrite)
)(unlift(Foo.unapply))

Play! could not find implicit value for parameter reducer

I'm following along the Play! 2.1 coast-to-coast tutorial at http://mandubian.com/2013/01/13/JSON-Coast-to-Coast/ but cannot get even the most trivial example working.
When I compile my project I get an error:
could not find implicit value for parameter reducer: play.api.libs.functional.Reducer[play.api.libs.json.JsString,B]
My controller code is as follows:
package controllers
import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
object MyController extends Controller{
val validate = (
(__ \ 'title).json.pick[JsString] and
(__ \ 'desc).json.pick[JsString]
).reduce
def test() = Action { implicit request =>
Ok("test")
}
}
What am I missing to get this working?
The syntax here is not quite right. 'pick' returns a JsValue (the Play! equivalent of valid Json types including String, Array, etc).
To validate multiple json fields you need to use 'pickBranch' which returns a JsObject (which is basically the equivalent of a Map[String, JsValue]). I'm guessing that reduce is a merge operation for several JsObjects.
I actually still haven't found a good use case for 'pick'. The '\' syntax seems to do the equivalent job with less code and clutter.