Fastest way to convert nested String to JSON [duplicate] - json

Is there a straight forward way to format a JSON string in scala?
I have a JSON String like this:
val json = {"foo": {"bar": {"baz": T}}}
Can I use a function f such that:
f(json) = {
"foo":
{"bar":
{"baz": T}
}
}
I know the formatting I have done in my answer is no perfect, but you get the point. And yes, can it be done without using Play Framework?

In case you are using Play Framework you could use Json.prettyPrint method to format JsValue:
import play.api.libs.json.Json
val str = """{"foo": {"bar": {"baz": "T"}}}"""
val jsValue = Json parse str
// JsValue = {"foo":{"bar":{"baz":"T"}}}
Json prettyPrint jsValue
// String =
// {
// "foo" : {
// "bar" : {
// "baz" : "T"
// }
// }
// }
In case you are using scala.util.parsing.json you have to create such method by yourself. For instance:
def format(t: Any, i: Int = 0): String = t match {
case o: JSONObject =>
o.obj.map{ case (k, v) =>
" "*(i+1) + JSONFormat.defaultFormatter(k) + ": " + format(v, i+1)
}.mkString("{\n", ",\n", "\n" + " "*i + "}")
case a: JSONArray =>
a.list.map{
e => " "*(i+1) + format(e, i+1)
}.mkString("[\n", ",\n", "\n" + " "*i + "]")
case _ => JSONFormat defaultFormatter t
}
val jsn = JSON.parseRaw("""{"foo": {"bar": {"baz": "T"}, "arr": [1, 2, "x"]}, "foo2": "a"}""").get
// JSONType = {"foo" : {"bar" : {"baz" : "T"}, "arr" : [1.0, 2.0, "x"]}, "foo2" : "a"}
format(jsn)
// String =
// {
// "foo": {
// "bar": {
// "baz": "T"
// },
// "arr": [
// 1.0,
// 2.0,
// "x"
// ]
// },
// "foo2": "a"
// }
Note that this is not an efficient implementation.

I thought I read somewhere that Typesafe was considering separating their JSON processing out of Play, so look into that to apply #senia's solution first.
Otherwise, look at Jackson--or more precisely, the Scala wrapper for Jackson:
val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
val writer = mapper.writerWithDefaultPrettyPrinter
val json = writer.writeValueAsString(Object value)
I've also heard that the kids are really into Scala Pickling, which apparently has pretty printing as well.

Related

Scala Argonaut folding across a list?

I'm very new to Argonaut (which I'm forced to use since the codebase is older, using an old version of scalaz, and we have no intentions to update it since we're rewriting this outdated code from scratch), and I'm trying to find a way to dump some data into JSON.
I have an case class called Magnitude. For simplicity, define it as:
case class Magnitude(bandname: String)
What I want is a function that takes in a List[Magnitude] and outputs Json that I can chain together with other Json.
Right now, I'm doing:
def magnitudeFields(magnitudes: List[Magnitude]): Json =
magnitudes.foldLeft(jEmptyObject) { case (j, m) =>
("bandName" := m.bandname) ->: j
}
and then say I have an instantiation of List[Magnitude]:
val magList = List(Magnitude("R"), Magnitude("J"), Magnitude("_uc"), Magnitude("K"))
when I call magnitudeFields(magList) in a call like:
... previousJsonData ->: ('magnitudes' := magnitudeFields(magList)) ->: nextJsonData ...
I only receive the bandName: K and the other three preceding entries are not part of the JSON output by the function:
"magnitudes" : {
"bandName" : "K"
},
instead of what I want, which is:
"magnitudes" : {
"bandName" : "R"
"bandName" : "J"
"bandName" : "_uc"
"bandName" : "K"
},
Any help would be greatly appreciated. I suspect I'm doing something wrong by using jEmptyObject as the default to the foldLeft and may need to use something like jArray at some point (with the function returning List[Json] instead, but I'm not sure how I should be doing this.
You should just define an EncodeJson instance for Magnitude and let Argonaut deal with the rest of the logic:
import argonaut._, Argonaut._
case class Magnitude(bandname: String)
object Magnitude {
implicit val encode: EncodeJson[Magnitude] =
EncodeJson { m =>
jSingleObject("bandName", jString(m.bandname))
}
}
object Main {
val magList = List(Magnitude("R"), Magnitude("J"), Magnitude("_uc"), Magnitude("K"))
def main(args: Array[String]): Unit = {
val prefix = "prefix" := "prefix"
val suffix = "suffix" := "suffix"
val json = prefix ->: ("magnitudes" := magList) ->: suffix ->: jEmptyObject
println(json.asJson.spaces2)
//{
// "prefix" : "prefix",
// "magnitudes" : [
// {
// "bandName" : "R"
// },
// {
// "bandName" : "J"
// },
// {
// "bandName" : "_uc"
// },
// {
// "bandName" : "K"
// }
// ],
// "suffix" : "suffix"
//}
}
}

Need to convert a string to Json Object in scala

I am trying to convert a string to JSON using JSON spray. But I am very new in scala and facing trouble to write the code. My input file is a string and may contain more element.
Example input String
12 rob 133 millan
Expected JSON file is below
[
{
"M": {
"Score": {
"N": "12"
},
"TopicID": {
"S": "rob"
}
}
},
{
"M": {
"Score": {
"N": "133"
},
"TopicID": {
"S": "milan"
}
}
}
Any suggestion toward code approach would help as well.
Please suggest.
Regards of creating the json:
First, you need to define the case class:
case class SClass(S: string)
case class NClass(N: string)
case class MClass(Score: NClass, TopicID: SClass)
Then:
implicit val mclassFormat = jsonFormat2(MClass)
implicit val nclassFormat = jsonFormat(NClass)
implicit val sclassFormat = jsonFormat(SClass)
// serialize the json (hardcoded values)
val mClass = MClass(NClass(12.toString(), SClass("rob"))
val mClassJsonString = mClass.toJson.prettyPrint
About parsing your input:
val input = "12 rob 13 bla"
val a = input.split(" ").zipWithIndex.collect{case(v,i) if (i % 2 == 0) =>
(v,i)}.map(_._1)
val b = input.split(" ").zipWithIndex.collect{case(v,i) if (i % 2 != 0) =>
(v,i)}.map(_._1)
val result = a.zip(b) // [(12,rob),(13,bla)]
now you can traverse on your result like this:
result.foreach{case (x,y) =>
val mClass = MClass(NClass(x.toString(), SClass(y))
val mClassJsonString = mClass.toJson.prettyPrint
}

How to map a JSON string to Kotlin Map

I have a simple Kotlin program that access a Mongo database and produce a JSON string as below;
"{
"_id" : { "$oid" : "593440eb7fa580d99d1abe85"} ,
"name" : "Firstname Secondname" ,
"reg_number" : "ATC/DCM/1016/230" ,
"oral" : 11 ,
"oral_percent" : 73 ,
"cat_1" : 57 ,
"cat_2" : 60 ,
"cat_average" : 59 ,
"assignment" : 90
}"
How do I map this in Kotlin Map/MutableMap? Is there an API in Kotlin to read JSON and map it to Map/MutableMap?
No additional library is needed:
val jsonObj = JSONObject(jsonString)
val map = jsonObj.toMap()
where toMap is:
fun JSONObject.toMap(): Map<String, *> = keys().asSequence().associateWith {
when (val value = this[it])
{
is JSONArray ->
{
val map = (0 until value.length()).associate { Pair(it.toString(), value[it]) }
JSONObject(map).toMap().values.toList()
}
is JSONObject -> value.toMap()
JSONObject.NULL -> null
else -> value
}
}
This can be done with Klaxon. With this you can easily read the Json data as JsonObject which is actually a MutableMap.
val json: JsonObject = Parser().parse(jsonData) as JsonObject
Using Jackson's kotlin module, you can create a Map/MutableMap as below:
val jsonString = "{\n" +
" \"_id\": {\n" +
" \"\$oid\": \"593440eb7fa580d99d1abe85\"\n" +
" },\n" +
" \"name\": \"Firstname Secondname\",\n" +
" \"reg_number\": \"ATC/DCM/1016/230\",\n" +
" \"oral\": 11,\n" +
" \"oral_percent\": 73,\n" +
" \"cat_1\": 57,\n" +
" \"cat_2\": 60,\n" +
" \"cat_average\": 59,\n" +
" \"assignment\": 90\n" +
"}"
val map = ObjectMapper().readValue<MutableMap<Any, Any>>(jsonString)
Note: In case you're getting the below compilation error
None of the following functions can be called with the arguments supplied
Please ensure that you have added the the dependency of jackson-module-kotlin (for gradle: compile "com.fasterxml.jackson.module:jackson-module-kotlin:${jackson_version}") and have added the import for the readValue implementation as import com.fasterxml.jackson.module.kotlin.readValue in the place where you're using readValue
This can be done without any third party library:
#Throws(JSONException::class)
fun JSONObject.toMap(): Map<String, Any> {
val map = mutableMapOf<String, Any>()
val keysItr: Iterator<String> = this.keys()
while (keysItr.hasNext()) {
val key = keysItr.next()
var value: Any = this.get(key)
when (value) {
is JSONArray -> value = value.toList()
is JSONObject -> value = value.toMap()
}
map[key] = value
}
return map
}
#Throws(JSONException::class)
fun JSONArray.toList(): List<Any> {
val list = mutableListOf<Any>()
for (i in 0 until this.length()) {
var value: Any = this[i]
when (value) {
is JSONArray -> value = value.toList()
is JSONObject -> value = value.toMap()
}
list.add(value)
}
return list
}
Usage to convert JSONObject to Map:
val jsonObject = JSONObject(jsonObjStr)
val map = jsonObject.toMap()
Usage to convert JSONArray to List:
val jsonArray = JSONArray(jsonArrStr)
val list = jsonArray.toList()
More info is here
This is now also possible with kotlinx.serialization:
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
val input = """{
"_id" : { "some_id" : "593440eb7fa580d99d1abe85"} ,
"name" : "Firstname Secondname" ,
"reg_number" : "ATC/DCM/1016/230" ,
"oral" : 11 ,
"oral_percent" : 73 ,
"cat_1" : 57 ,
"cat_2" : 60 ,
"cat_average" : 59 ,
"assignment" : 90
}"""
val json = Json.parseToJsonElement(input)
val map = json.jsonObject.toMap()
The latest version of the org.json library has JSONObject.toMap() so you can just do
val map = JSONObject(string).toMap()
So just add the org.json:json:20220924 dependency to your project.
If you're writing for Android (and thus have an old version of the org.json library) then the following can be used.
import org.json.JSONArray
import org.json.JSONObject
fun JSONObject.toMap(): Map<String, Any?> =
keys().asSequence().associateWith { key -> toValue(get(key)) }
fun JSONArray.toList(): List<Any?> =
(0 until length()).map { index -> toValue(get(index)) }
private fun toValue(element: Any) = when (element) {
JSONObject.NULL -> null
is JSONObject -> element.toMap()
is JSONArray -> element.toList()
else -> element
}

Parsing/serializing JSON with Scala

In Scala, how can I de-serialize a JSON string, modify a value and serialize back to string?
There has to be a way to do this without using 3rd party libraries but I can't get it to work. Here is what I tried so far:
import scala.util.parsing.json
var lines = "{\"id\" : \"abc\", \"stuff\" : [1, 2, 3], \"more\" : {\"bro\" : \"science\"}}"
// Test 1
val myJSON = json.JSON.parseRaw(lines)
// myJSON: Option[scala.util.parsing.json.JSONType] = Some({"id" : "abc", "stuff" : [1.0, 2.0, 3.0], "more" : {"bro" : "science"}})
// I cannot modify fields on the JSONType instance but toString() works well.
// res1: String = Some({"id" : "abc", "stuff" : [1.0, 2.0, 3.0], "more" : {"bro" : "science"}})
// Test 2
// This way I can parse JSON into a map and manipulate its values.
// val myMap = json.JSON.parseFull(lines).get.asInstanceOf[Map[String, Any]] + ("id" -> "blah")
// myMap: scala.collection.immutable.Map[String,Any] = Map(id -> blah, stuff -> List(1.0, 2.0, 3.0), more -> Map(bro -> science))
// However, when converted to an instance of JSONObject and calling
// toString() only the top-level items are JSON-serialized
new json.JSONObject(myMap).toString()
// res2: String = {"id" : "blah", "stuff" : List(1.0, 2.0, 3.0), "more" : Map(bro -> science)}
If it's not possible with standard Scala, I'd appreciate and example of how to do this with a third party library.
Thanks,
/David
small silly/trivial example of what I mentioned. Could be written better too but wanted to break it into chunks. There is a lot you can do with them:
Here is old link in terms of play version but as far as I know up to date on features available in 2.3.x:
https://www.playframework.com/documentation/2.1.1/ScalaJsonTransformers
import play.api.libs.json._
var lines = "{\"id\" : \"abc\", \"stuff\" : [1, 2, 3], \"more\" : {\"bro\" : \"science\"}}"
val jsonAsJsValue = Json.parse(lines)
//jsonAsJsValue: play.api.libs.json.JsValue = {"id":"abc","stuff": [1,2,3],"more":{"bro":"science"}}
val updateIdTransformer = (__ \"id").json.update(
__.read[JsString].map{a => JsString("def")}
)
val updatedJson = jsonAsJsValue.transform(updateIdTransformer)
//updatedJson: play.api.libs.json.JsResult[play.api.libs.json.JsObject] = JsSuccess({"id":"def","stuff":[1,2,3],"more":{"bro":"science"}},/id)

Format JSON string in scala

Is there a straight forward way to format a JSON string in scala?
I have a JSON String like this:
val json = {"foo": {"bar": {"baz": T}}}
Can I use a function f such that:
f(json) = {
"foo":
{"bar":
{"baz": T}
}
}
I know the formatting I have done in my answer is no perfect, but you get the point. And yes, can it be done without using Play Framework?
In case you are using Play Framework you could use Json.prettyPrint method to format JsValue:
import play.api.libs.json.Json
val str = """{"foo": {"bar": {"baz": "T"}}}"""
val jsValue = Json parse str
// JsValue = {"foo":{"bar":{"baz":"T"}}}
Json prettyPrint jsValue
// String =
// {
// "foo" : {
// "bar" : {
// "baz" : "T"
// }
// }
// }
In case you are using scala.util.parsing.json you have to create such method by yourself. For instance:
def format(t: Any, i: Int = 0): String = t match {
case o: JSONObject =>
o.obj.map{ case (k, v) =>
" "*(i+1) + JSONFormat.defaultFormatter(k) + ": " + format(v, i+1)
}.mkString("{\n", ",\n", "\n" + " "*i + "}")
case a: JSONArray =>
a.list.map{
e => " "*(i+1) + format(e, i+1)
}.mkString("[\n", ",\n", "\n" + " "*i + "]")
case _ => JSONFormat defaultFormatter t
}
val jsn = JSON.parseRaw("""{"foo": {"bar": {"baz": "T"}, "arr": [1, 2, "x"]}, "foo2": "a"}""").get
// JSONType = {"foo" : {"bar" : {"baz" : "T"}, "arr" : [1.0, 2.0, "x"]}, "foo2" : "a"}
format(jsn)
// String =
// {
// "foo": {
// "bar": {
// "baz": "T"
// },
// "arr": [
// 1.0,
// 2.0,
// "x"
// ]
// },
// "foo2": "a"
// }
Note that this is not an efficient implementation.
I thought I read somewhere that Typesafe was considering separating their JSON processing out of Play, so look into that to apply #senia's solution first.
Otherwise, look at Jackson--or more precisely, the Scala wrapper for Jackson:
val mapper = new ObjectMapper()
mapper.registerModule(DefaultScalaModule)
val writer = mapper.writerWithDefaultPrettyPrinter
val json = writer.writeValueAsString(Object value)
I've also heard that the kids are really into Scala Pickling, which apparently has pretty printing as well.