Fetch the json from HttpEntity - json

I have an object of akka.http.scaladsl.model.HttpEntity looking like HttpEntity("application/json", {Myjson here})
Is there a way i can fetch my json from the entity without any string manipulations(other than converting to a string and doing a split)

You will need a JSON parser and a glue code between the JSON parser and Akka Http called Unmarshaller.
Akka Http includes unmarshallers for spray-json library. You can find more unmarshallers in hseeberger/akka-http-json library.
If you choose to use spray-json a pseudo code for that would be
case class MyJson(v1: String, v2: Int)
object JsonProtocol extends DefaultJsonProtocol {
implicit val myFormat = jsonFormat2(MyJson)
}
val resp: Future[MyJson] = Unmarshal(response).to[MyJson]

Related

Kotlin Serialization - Decoding JSON Array from string

Trying to deserialize cached json string to data object and getting exception: kotlinx.serialization.json.internal.JsonDecodingException: Expected class kotlinx.serialization.json.JsonObject (Kotlin reflection is not available) as the serialized body of kotlinx.serialization.Polymorphic<List>, but had class kotlinx.serialization.json.JsonArray (Kotlin reflection is not available)
Code used to deserialize
internal inline fun <reified R : Any> String.convertToDataClass() =
Json {
ignoreUnknownKeys = true
}.decodeFromString(R::class.serializer(), this)
Code example:
val jsonString ="""
[{"name1":"value1"}, {"name2":"value2"}]
"""
val dataObject = jsonString.convertToDataClass<List<SomeObject>>()
When going through Ktor pipeline everything works fine but it is breaking on attempt to deserialize the same response body cached as string.
I am aware of that R::class.serializer() is marked as for internal usage but this is the only way known to me how to deserialize generics from string content.
There is a fitting extension function available at kotlinx.serialization.decodeFromString that takes one generic parameter, so you could pass R as generic to that extension.
Check https://github.com/Kotlin/kotlinx.serialization#introduction-and-references. The sample is val obj = Json.decodeFromString<Project>(string), which will fit your needs doing something like this
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
internal inline fun <reified R : Any> String.convertToDataClass() =
Json {
ignoreUnknownKeys = true
}.decodeFromString<R>(this)

Cannot find JsonReader or JsonFormat for class. Multiple Formats inside a Protocol

I am using spray-json library to deserialize json objects. I want to be able to parse a DateTime object, specified in the nscala-time library, from a json string.
I want to deserialize a json string of this form:
{"updatedAt": "2015-05-18T23:55:49.033Z"}
For this I am defining my own protocol using spray-json's approach. My spray-json protocol goes as follows:
import spray.json._
// I want to de-serialize an object from this library
import com.github.nscala_time.time.Imports._
object CustomProtocol extends DefaultJsonProtocol {
// There are other implicit objects that describe how to format other
// classes before the one below. All of them are independent from each other
implicit object DateTimeFormat extends RootJsonFormat[DateTime] {
def write(dateTime: DateTime) = JsString(dateTime.toString)
def read(json: JsValue) = {
json.asJsObject.getFields("updatedAt") match {
case Seq(JsString(timeStamp)) => DateTime.parse(timeStamp)
case other => deserializationError("DateTime expected, found \n" + other)
}
}
}
My test (using scala-test) goes as follows:
//Previous imports
import CustomProtocol._
test("Can parse DateTime from Json string") {
val DateTimeJson = """ {"updatedAt": "2015-05-18T23:55:49.033Z"} """
val DateTimefromJson = DateTimeJson.parseJson.convertTo[DateTime]
val realDateTime = DateTime.parse("2015-05-18T23:55:49.033Z")
assert(DateTimefromJson.equals(realDateTime))
I get the following compilation error:
{Directory}/src/test/scala/messageJsonParserTest.scala:29: Cannot find
JsonReader or JsonFormat type class for com.github.nscala_time.time.Imports.DateTime
[error] val DateTimefromJson = DateTimeJson.parseJson.convertTo[DateTime]
Can anybody spot the error in my code?
I'm sorry if this question has been asked before. After browsing my mind and the internet for a whole day I have not been able to come up with a solution.

How do I convert a Scala List[org.bson.Document] to a JSON String?

I had a function in AWS Lambda:
def test(pj: Pojo, context: Context): java.util.List[Document]
that was not initializing the pj with the input JSON values at all.
I found another way of doing AWS Lambda in Scala like this:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
val scalaMapper = new ObjectMapper().registerModule(new DefaultScalaModule)
def test(input: InputStream, output: OutputStream): Unit = {
val inputPojo = scalaMapper.readValue(input, classOf[Pojo])
val answer: Seq[Document] = getTheRealAnswer(inputPojo)
val jsonStr = "{ frustration: \"I wish my answer was JSON.\" }"
output.write(jsonStr.getBytes("UTF-8"))
}
and that works, except what I really want to return as an answer is a JSON array of Documents. How should I go about that?
Edit: In my original posting, I wrote: "[the first example] was returning the answer as an error 22. Basically AWS (I think) treated the JSON conversion of the List[Document] as a filename, JSON has plenty of colons, and the error 22 came from colons in filenames not being allowed. Weird." That turned out to be an error in my invocation of the AWS Lambda Function from AWS CLI. I omitted the output filename in the command invocation, and returned JSON was interpreted by AWS CLI as a filename.
Since I wrote this message, I got things to work like this:
def jsonizeDocs(cDocument: Seq[Document]): String = {
val sb=new StringBuilder
for (doc <- cDocument) {
if (sb.nonEmpty) {
sb.append(",")
}
sb.append(doc.toJson)
}
sb.toString
}
Note! This answer is based on a light wrapper I wrote around json4s which I call JSON Extensions
Assuming you are using Scala Objects, import the io.onema.json.Extensions._
import io.onema.json.Extensions._
case class Doc(title: String, content: String)
val listOfDocs = Seq(Doc("Foo", "bar"), Doc("Bar", "Baz"), Doc("Blah", "Bax"))
val json: String = listOfDocs.asJson
println(json)
// [{"title":"Foo","content":"bar"},{"title":"Bar","content":"Baz"},{"title":"Blah","content":"Bax"}]
See the running example here
Now, since you are using a Pojo, you need to import io.onema.json.JavaExtensions._. Assuming you have the following POJO:
public class Document {
private String title;
private String content;
public String getTitle() {return title;}
public String getContent() {return content;}
public void setTitle(String title) { this.title = title;}
public void setContent(String content) {this.content = content;}
}
Use this method in your Scala code like such:
import io.onema.json.JavaExtensions._
import com.example.Document
// ...
def jsonizeDocs(cDocument: Seq[Document]): String = {
val json: String = cDocument.asJson
println(json)
json
}
In AWS Lambda (and to go the other way around) use jsonDecode and a custom object mapper to deserialize to the expected type:
import io.onema.json.JavaExtensions._
import io.onema.json.Mapper
import com.example.Document
val jsonString = """[{"title":"Foo","content":"bar"},{"title":"Bar","content":"Baz"},{"title":"Blah","content":"Bax"}]"""
val mapper: ObjectMapper = Mapper.allowUnknownPropertiesMapper
val doc: Document = jsonString.jsonDecode[Document](mapper)
I have used the method described here quite successfully in a lambda framework that is able to deserialize to AWS lambda events as well as custom types, see a simple example here.
That's it! you can use this library or one of the many JSON serializers in Java or Scala. If you know the type of your objects most libraries will enable you to serialize to JSON and back very easily.

Custom akka persistence (Play!) JSON (de)serializer

I have my relevant actors' messages (de)serializable to/from Play! JSON. I'd like to use JSON (de)serializers for akka persistance system (if possbile).
In akka persistance documentation there is possibility to use our own serializers. Further more here are the instructions how to write custom serializers. Since akka.serialization.Serializer is expecting toBinary and fromBinary is there any way to use Play JSON serializers with akka persistence?
Thank you!
Best!
Where do you like to serialize data to?
I'm looking for a mongodb based akka persistence store with serialized objects using the json formats on my own. Maybe the following driver might be interesting for you as well:
https://github.com/scullxbones/akka-persistence-mongo/issues/16
Integrating play json into akka-persistence is complicated since play json uses instances of Format that are gathered via implicits. Akka provides just java.lang.Object for serialization and a java.lang.Class[_] for deserialization which makes resolving the correct implicit Format impossible.
What you could do is write a custom akka.serialization.Serializer that has a Map from Class[A] to Format[A]. This map can be used to find the correct format for a java.lang.Object / java.lang.Class[_]:
class JsonSerializer(serializers: Map[Class[_], Format[_]]) extends Serializer {
val charset: Charset = StandardCharsets.UTF_8
val identifier: Int = "play-json-serializer".##
val includeManifest: Boolean = true
def serializer[A](c: Class[_]): GenericFormat[A] = serializers.get(c) match {
case Some(format) => format.asInstanceOf[GenericFormat[A]]
case None => throw new RuntimeException("No Format available for " + c.getName)
}
def toBinary(o: AnyRef): Array[Byte] = jsonSerialize(o).getBytes(charset)
def fromBinary(bytes: Array[Byte], manifest: Option[Class[_]]): AnyRef = jsonDeserialize(bytes, manifest.get)
def jsonSerialize[A](a: A): String = {
implicit val format: GenericFormat[A] = serializer[A](a.getClass)
Json.stringify(Json.toJson(a))
}
def jsonDeserialize[A](bytes: Array[Byte], manifest: Class[_]): A = {
implicit val format: GenericFormat[A] = serializer[A](manifest)
Json.fromJson[A](Json.parse(new String(bytes, charset))).get
}
}
You can now inherit this class and pass play formats for all types that your akka-serializer should be able to (de)serialize to the constructor. This serializer must be configured in the akka configuration as described in the documentation:
class MyJsonSerializer extends JsonSerializer(Map(
Serializer[Foo], Serializer[...], ...
))
// Just a utility class for the pretty syntax above
object Serializer {
def apply[A](implicit format: Format[A], ctag: ClassTag[A]): (Class[A], Format[A]) =
(ctag.runtimeClass.asInstanceOf[Class[A]], format)
}

Parsing JSON with multiple tuples to List<object> in scala

[
{"fname":"Foo","lname":"Pacman"},
{"fname":"Bar","lname":"Mario"},
{"fname":"Poo","lname":"Wario"}
]
Well I have JSON string in this format,
Now what I need is to convert each tuples -> {"fname":"Foo","lname":"Pacman"}
To a Person object,
for e.g. lets assume I have a case class
case class Person(fname:String,lname:String)
Now how am I to get, List<person>
If I had a JSON containing data for single tuple, then I could,
val o:Person = parse[Person](jsonString)// I am actually using Jerkson Lib
But since there are more than one tuples, how am i to parse them individually and create objects and create a list.
Jerkson supports deserializing lists of objects out of the box, so all you should need to do is:
val people = parse[List[Person]](personJson)
You can use json4s (which is a wrapper around either jackson or lift-json) where you also get such parsing capabilities out of the box.
import org.json4s._
import org.json4s.jackson.JsonMethods._
implicit val formats = DefaultFormats
val personJson = """
[
{"fname":"Foo","lname":"Pacman"},
{"fname":"Bar","lname":"Mario"},
{"fname":"Poo","lname":"Wario"}
]"""
case class Person(fname:String,lname:String)
val people = parse(personJson).extract[List[Person]]