I am trying to create Akka Http REST post endpoint mapping the JSON objects to the case class defined
import io.circe.Decoder, io.circe.generic.auto._
case class JobEntity(id: Option[Long] = None, name: String, description: String, json_data :java.sql.blob)
The JSON is of type
{
"id": "124",
"name": "MYJOB",
"description": "Test Job"
}
Now I want to map whole JSON to the 'json_data' as blob defined in the case class
post {
entity(as[JobEntity]) { jobEntity: JobEntity =>
complete(createJob(jobEntity).map(_.asJson))
}
}
I understand .map(_.asJson) would map the json to the JobEntity, Correct me if Its not like this
How do I map the whole JSON to the json_data.
You need to import Circe support methods. See the example . And you need to add dependency de.heikoseeberger % akka-http-circe.
Related
Imagine I have a json (in string format) that looked like:
val jsonString: String = "
{
"toys": {
"orange toys": {
"brand":"Toys-R-Us",
"price":"123.45"
}
},
"date":"05-27-1996"
}
"
My question is, how can I get the value for "brand" (Toys-R-Us), in Scala, using the scala.util.parsing.json library? I am assuming that this might require the traversing of the json or maybe even easier, a way to look up the key "brand" and from their obtaining the value.
The scala.util.parsing.json library is outdated, unsafe for open systems, and not included in the latest version of the Scala library.
Use dijon FTW!
import dijon._
import scala.language.dynamics._
val jsonString: String = """
{
"toys": {
"orange toys": {
"brand":"Toys-R-Us",
"price":"123.45"
}
},
"date":"05-27-1996"
}
"""
println(parse(jsonString).toys.`orange toys`.brand)
Here is a runnable code on Scastie that works fine with Scala 2 and Scala 3.
I'm looking to rewrite a spring javafx application in Kotlin using Ktor. I am new to both Spring and Ktor.
I'm having trouble using Ktor to figure out how to approach serialization and deserialisation of POJOs. (These POJOs are part of a common library).
The API I'm trying to serve requests to with Ktor uses:
Hibernate ORM as ORM mapper
Elide with RSQL filters to serve JSON-API conform data
For example "data/event" would return:
{
"data": [
{
"type": "event",
"id": "15b6c19a-6084-4e82-ada9-6c30e282191f",
"attributes": {
"imageUrl": null,
"name": "some text",
"type": "NUMERIC"
}
}, // and more event objects
]
}
Looking at the codebase in the spring application, it looks like they are using a RestTemplate to deserialise the above into an Event class (which only has an id, imageUrl, name and type as variables). Spring seems to automatically know how to get a POJO from JSON-API.
How can I do the same with Ktor? I tried the simplest:
val response = client.request<List<Event>>("data/event")
With the serialiser as gson:
install(JsonFeature) {
serializer = GsonSerializer()
}
But this results in a list of Event objects with none of their variables correctly set.
I have to make a wrapper class:
data class MyWrapper(val data: List<Event>)
And with that it will populate the list with the objects id set correctly, but none of the other attributes. So by default it looks like Ktor isnt configured for JSON-API. How can I change this?
I believe JSON:API is not supported out of the box. You need to write your own serializer, or use another wrapper class:
class JSONAPI<T>(val type: String, val id: String, val attributes: T)
data class MyWrapper(val data: List<JSONAPI<Event>>)
The problem here is that all fields in Event except of id will be filled. So you will need to adjust deserialization result:
val response: List<Event> = client.request<MyWrapper>("data/event").data.filterNotNull().map { it.attributes.copy(id = it.id) }
I am writing a small scala practice code where my input is going to be in the fashion -
{
"code": "",
"unique ID": "",
"count": "",
"names": [
{
"Matt": {
"name": "Matt",
"properties": [
"a",
"b",
"c"
],
"fav-colour": "red"
},
"jack": {
"name": "jack",
"properties": [
"a",
"b"
],
"fav-colour": "blue"
}
}
]
}
I'll be passing this file as an command line argument.
I want to know that how do I accept the input file parse the json and use the json keys in my code?
You may use a json library such as play-json to parse the json content.
You could either operate on the json AST or you could write case classes that have the same structure as your json file and let them be parsed.
You can find the documentation of the library here.
You'll first have to add playjson as depedency to your project. If you're using sbt, just add to your build.sbt file:
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.13"
Play json using AST
Let's read the input file:
import play.api.libs.json.Json
object Main extends App {
// first we'll need a inputstream of your json object
// this should be familiar if you know java.
val in = new FileInputStream(args(0))
// now we'll let play-json parse it
val json = Json.parse(in)
}
Let's extract some fields from the AST:
val code = (json \ "code").as[String]
val uniqueID = (json \ "unique ID").as[UUID]
for {
JsObject(nameMap) ← (json \ "names").as[Seq[JsObject]]
(name, userMeta) ← nameMap // nameMap is a Map[String, JsValue]
} println(s"User $name has the favorite color ${(userMeta \ "fav-colour").as[String]}")
Using Deserialization
As I've just described, we may create case classes that represent your structure:
case class InputFile(code: String, `unique ID`: UUID, count: String, names: Seq[Map[String, UserData]])
case class UserData(name: String, properties: Seq[String], `fav-colour`: String)
In addition you'll need to define an implicit Format e.g. in the companion object of each case class. Instead of writing it by hand you can use the Json.format macro that derives it for you:
object UserData {
implicit val format: OFormat[UserData] = Json.format[UserData]
}
object InputFile {
implicit val format: OFormat[InputFile] = Json.format[InputFile]
}
You can now deserialize your json object:
val argumentData = json.as[InputFile]
I generally prefer this approach but in your case the json structure does not fit really well. One improvement could be to add an additional getter to your InputFile class that makes accesing the fields with space and similar in the name easier:
case class InputFile(code: String, `unique ID`: UUID, count: String, names: Seq[Map[String, String]]) {
// this method is nicer to use
def uniqueId = `unique ID`
}
First, I searched a lot on Google and StackOverflow for questions like that, but I didn't find any useful answers (to my big surprise).
I saw something about Play Framework, how to create JSON array in Java and how to create JSON objects in Java, but I don't want to use Play Framework and I don't know if the creation of JSON objects differ from Scala to Java.
Following is the JSON I want to create. Later I'll convert the object into a string to send it via a POST request (through an API call).
{
"start_relative": {
"value": "5",
"unit": "years"
},
"metrics": [
{
"name": "DP_391366" # S-Temperature - Celsius
},
{
"name": "DP_812682" # Sensor-A4 Luminosity
}
]
}
How can I do something like that in Scala?
You should use a library that handles serialization/deserialization.
I would consider choosing between Spray Json and Play Json.
I will explain to you how the process works with Play first, and it's very similar to that in Spray.
Let's say you have a class, and an object with an instance and a json as string:
case class MyClass(id: Int,
name: String,
description: String)
object Data {
val obj: MyClass = MyClass(1, "me", "awesome")
val str: String =
"""
|{
| "id": 1,
| "name": "me",
| "description": "awesome"
|}
""".stripMargin
}
For MyClass to be serialized/deserialized, you will need an implicit formatter, specific for this, so you will create an object that contains this formatter using Play.
trait MyClassPlayProtocol {
implicit val formatAbility = Json.format[Ability]
}
object MyClassPlayProtocol extends MyClassPlayProtocol
The serialization/deserialization will look something like this:
object PlayData {
import play.api.libs.json.JsValue
import play.api.libs.json.Json
import MyClassPlayProtocol._
import General._
val str2Json: JsValue = Json.parse(str)
val obj2Json: JsValue = Json.toJson(obj)
val json2Str: String = Json.stringify(str2Json)
val json2Obj: MyClass = obj2Json.as[MyClass]
}
In Spray, the protocol will look like this:
trait MyClassSprayProtocol extends DefaultJsonProtocol {
implicit val myClassFormat = jsonFormat3(MyClass)
}
object MyClassSprayProtocol extends MyClassSprayProtocol
and the serialization/deserialization:
object SprayData {
import spray.json._
import MyClassSprayProtocol._
import General._
val str2Json: JsValue = str.parseJson
val obj2Json: JsValue = obj.toJson
val json2Str: String = str2Json.compactPrint
val json2Obj: MyClass = obj2Json.convertTo[MyClass]
}
As you can see, it's mostly a matter of choice between this two. Both are still improved and probably will be in the near future.
Depending on the benchmark, you will find that one is better than the other by a few miliseconds (usually Spray).
I for one am using Spray at work and Play in some personal projects, and I can't say I found something fundamentally different from one to another.
EDIT:
And to finally answer your question, to go from MyClass to String (serialization), you will do something like this:
PLAY: Json.stringify(Json.toJson(myClass))
SPRAY: myClass.toJson.compactPrint
And the deserialization:
PLAY: Json.parse(string).as[MyClass]
SPRAY: myClass.parseJson.convertTo[MyClass]
You need to use a library if you dont want it to do by yourself there are serveral:
Spray Json - https://github.com/spray/spray-json
Lift Json - https://github.com/lift/lift/tree/master/framework/lift-base/lift-json/
Jerkson - https://github.com/codahale/jerkson
Jackson - You can use Jackson with the scala Module https://github.com/FasterXML/jackson-module-scala
Note: The cool Java Gson LIbrary looses a lot of Magic if you want to use it with Scala, because they dont know Collections. So if you want to use this library you have to convert the "Scala List" to java.util.List and after that use Gson.
I'm invoking a web service that returns JSON.
The service returns either one of the following response.
case 1:
JSON:
[ {"name":"somevalue1", "key1":"value1", "key2":"value2"},
{"name":"somevalue1", "key1":"value1", "key2":"value2"},
{"name":"somevalue1", "key1":"value1", "key2":"value2"} ]
case class:
case class ValidResponse(name: String, key1: String, key2: String)
case 2:
JSON:
{"name": "invalid-response"}
Case class:
case class InvalidResponse(name:String)
I'm using json4s to parse the response as follows:
val parsedRes = parse(responseJson)
val objs: List[ValidResponse] = j.extract[List[ValidResponse]]
This works if the response string is the json in case 1. However, I get a parsedException in case the response string contains the json in case 2.
How can I handle response of multiple types?
A better way is to use one common class for both types of responses (valid and invalid):
case class Response(name: String, key1: Option[String], key2: Option[String]).
Play Framework has a great JSON parser. You can utilize this. Note that your Scala project does not have to be a Play project. You just need to import the library.
https://www.playframework.com/documentation/2.4.x/ScalaJson