Modify json field type via circe - json

I have simple Json:
val str = """{"test":"123"}"""
How I can modify String "123" to Int 123 to get new Json?:
{"test":123}
Now I am using:
val json = parse(str).getOrElse(Json.Null)
val jsObj = json.asObject.get // Unsafe, just example
val newJson = Json.fromJsonObject(jsObj.remove("test").add("test", Json.fromInt(123)))
But this code is not pretty.
Is it possible to make this code prettier or maybe do it via circe optics?

It should do the trick depending on how you want to manage the limit case (here I throw an exception):
import io.circe._
import io.circe.parser.parse
val str = """{"test":"123"}"""
val json = parse(str).getOrElse(Json.Null)
json.mapObject(
_.mapValues( v =>
v.asString
.flatMap(parse(_).toOption)
.getOrElse(throw new IllegalArgumentException("No String found"))
)
)

Related

Kotlin - parsing json string throws MalformedJsonException: Unterminated object

I am trying to save a value to a jsonb DB column:
val deliveryAddressAsJson = deliveryAddress?.toJson()
val lat = deliveryAddressAsJson?.get("latitude")
val lng = deliveryAddressAsJson?.get("longitude")
val dataJson = jsonObject("comment" to "KARTKOORD:#LE#$lat#$lng# #")
val values = mapOf(
"type" to EventType.RESOLVED.dbName,
"created_by" to ctx.userId,
"data" to dataJson.toPgObject(),
"package_id" to packageId
)
#Language("PostgreSQL")
val sql = """insert into package_event(type, created_by, data, package_id) values (:type, :created_by, :data, :package_id)""".trimMargin()
insert(ctx, sql, values).bind()
I can see that the data is saved like this:
data -> {Collections$SingletonMap#6348} size = 1
key = "data"
value = {Collections$SingletonMap#6348} size = 1
key = "comment"
value = "KARTKOORD:#LE#59.8098962#10.7809297# #"
But, if I try to parse it:
val resolvedPackageEvent = fetchRows(ctx, queryOf("select * from package_event where package_id = ? and type = 'resolved'", packageId)).first()
val data = parseJson(resolvedPackageEvent.string("data"))
val deliveryAddress = data.get("comment")
I get an exception thrown:
com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Unterminated object at line 1 column 20 path $.comment
If I try to get the value with jsonObject method like this:
fun Entity.jsonObject(key: String): JsonObject = when (val v = this[key]) {
is String -> parseJson(v)
else -> v as JsonObject
}
resolvedPackageEvent.jsonObject("data")
I get an exception:
java.lang.ClassCastException: class java.util.Collections$SingletonMap cannot be cast to class com.google.gson.JsonObject (java.util.Collections$SingletonMap is in module java.base of loader 'bootstrap'; com.google.gson.JsonObject is in unnamed module of loader 'app')
How should I parse this json string?
Your data is not an String. As you can see in the debugger, your data is something like
{
"data": {
"comment": "KARTKOORD:#LE#59.8098962#10.7809297# #"
}
}
I don't have the gson syntax at hand, so I can't provide precise code for retrieving the value. You can probably do something like
val result: JsonObject = parseJson(resolvedConsignmentEvent)
val data: JsonObject = result.get("data")
val deliveryAddress: String = data.get("comment")

How I can sort list of JObject (Scala, json4s)

I read a JSON file and use json4s to parse JSON.
How I can sort an array of JObject that I read from the file?
package org.example
import org.json4s._
import org.json4s.native.JsonMethods._
object Main extends App {
val file = scala.io.Source.fromURL("https://raw.githubusercontent.com/mledoze/countries/master/countries.json")
val text = file.mkString
val json = parse(text)
val countries = json.children
countries.sortBy(...)
}
Each country has a field "area" and I need to sort the list by this field.
I don't know how to use sortBy in this situation because the country is not just an object of some class. This is JObject and the field "area" has a type JInt.
Can you help me? Or maybe do you know other solution?
Thanks, Gaƫl J for Hint.
I solved my problem as follows:
package org.example
import org.json4s._
import org.json4s.native.JsonMethods._
object Main extends App {
val file = scala.io.Source.fromURL("https://raw.githubusercontent.com/mledoze/countries/master/countries.json")
val text = file.mkString
val json = parse(text)
val countries = json.children
val countriesSorted = countries.sortWith((x: JValue, y: JValue) => {
//File has a few countries with area that has type of Double
def getValue(value: JValue): Double = {
value \ "area" match {
case JDouble(value) => value
case JInt(value) => value.toDouble
case _ => 0
}
}
val xValue = getValue(x)
val yValue = getValue(y)
xValue > yValue
})
for (country <- countriesSorted)
println(country \ "area")
}

Compare key and values in json string using scala

Want to compare the first json string with the other 2 json string.
First the keys should match . If they match , then compare the nested key and values.
val of1 = "{\"keyA\":{\"1\":13,\"0\":202}}"
val of2 = "{\"keyA\":{\"1\":12,\"0\":201}}"
val of3 = "{\"keyB\":{\"1\":12}}"
Should throw Error for key mismatch.
val of1 = "{\"keyA\":{\"1\":13,\"0\":202}}"
val of2 = "{\"keyA\":{\"1\":12,\"0\":201}}"
val of2 = "{\"keyA\":{\"1\":11,\"0\":200}}"
This should return true, as both keys match and also sub keys 1 and 0 have more values than sub key of json 2 and json 3.The numbers are Long values.
Please help.
Below is my try.
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper
val of1 = "{\"keyA\":{\"1\":13,\"0\":202}}"
val of2 = "{\"keyA\":{\"1\":12,\"0\":201}}"
val of3 = "{\"keyB\":{\"1\":12}}"
def OffsetComparator(json1: String, json2: String, json3:String): Boolean = {
val mapper = new ObjectMapper() with ScalaObjectMapper
mapper.registerModule(DefaultScalaModule)
val jsonObj1 = mapper.readValue(json1, classOf[Map[String, Map[String, Long]]])
val jsonObj2 = mapper.readValue(json2, classOf[Map[String, Map[String, Long]]])
val jsonObj3 = mapper.readValue(json3, classOf[Map[String, Map[String, Long]]])
//Trying to get the key and compare first
val mapA = jsonObj1.keySet.foreach(i=>jsonObj1.keySet(i).toString)
val mapB = jsonObj2.keySet
val mapC = jsonObj3.keySet
println( (jsonObj1.keySet == jsonObj3.keySet) )
if (mapA.keySet != mapB.keySet || mapA.keySet != mapC.keySet) throw new Exception("partitions mismatch")
mapA.keys.forall(k => (mapA(k).asInstanceOf[Long] > mapB(k).asInstanceOf[Long] && mapA(k).asInstanceOf[Long] > mapC(k).asInstanceOf[Long]))
// getting error :java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long when i am casting as Long.Not su
}
println(OffsetComparator(of1, of2,of3))
}
You can try with https://github.com/gnieh/diffson. ITs available for circe, spray-json and play-json.
Your example with Circe:
import diffson._
import diffson.lcs._
import diffson.jsonpatch.lcsdiff._
import io.circe._
import diffson.circe._
import diffson.jsonpatch._
import io.circe.parser._
val decoder = Decoder[JsonPatch[Json]]
val encoder = Encoder[JsonPatch[Json]]
implicit val lcs = new Patience[Json]
val json1 = parse(of1)
val json2 = parse(of2)
val patch =
for {
json1 <- json1
json2 <- json2
} yield diff(json1, json2)
print(patch)
That gives:
Right(JsonPatch(List(Replace(Chain(Left(keyA), Left(0)),201,None), Replace(Chain(Left(keyA), Left(1)),12,None))))
take a look to see how it works https://index.scala-lang.org/gnieh/diffson/diffson-circe/4.0.3?target=_2.13
For Circe, indlude the dependence:
"org.gnieh" %% f"diffson-circe" % "4.0.3"

Find the maximum value from JSON data in Scala

I am very new to programming in Scala. I am writing a test program to get maximum value from JSON data. I have following code:
import scala.io.Source
import scala.util.parsing.json._
object jsonParsing{
//Id int `json:"id"`
//Price int `json:"price"`
def main(args: Array[String]): Unit = {
val file_name = "jsonData.txt"
val json_string = scala.io.Source.fromFile("jsonData.txt").getLines.mkString
val json_arr = json_string.split(",")
json_arr.foreach {println}
}
}
The json_arr.foreach {println} prints following data:
[{ "id":1
"price":4629}
{ "id":2
"price":7126}
{ "id":3
"price":8862}
{ "id":4
"price":8999}
{ "id":5
"price":1095}]
I am stuck at the part of figuring out how to find the maximum price from such JSON data? That is, in this case the output should be '8999'.
you can try something like this below:
package com.x.x.integration.commons
import collection.immutable.IndexedSeq
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
case class wrapperObject(val json_string: Array[MyJsonObject])
case class MyJsonObject(val id:Int ,val price:Int)
object Demo {
val gson = new Gson()
def main(args: Array[String])={
val json_string = scala.io.Source.fromFile("jsonData.txt").getLines.mkString
//val json_string= """{"json_string":[{"id":1,"price":4629},{"id":2,"price":7126},{"id":3,"price":8862},{"id":4,"price":8999},{"id":5,"price":1095}]}"""
val jsonStringAsObject= new JsonParser().parse(json_string).getAsJsonObject
val objectThatYouCanPlayWith:wrapperObject = gson.fromJson(jsonStringAsObject, classOf[wrapperObject])
var maxPrice:Int = 0
for(i <- objectThatYouCanPlayWith.json_string if i.price>maxPrice)
{
maxPrice= i.price
}
println(maxPrice)
}
}
check if it helps you
I also recommend to use Json4s or playJson.
But you could do without any libraries as such.
val json = """[{"id":1,"price":100},{"id":2, "price": 200}]"""
val priceRegex = """"price"\s*:\s*(\d+)""".r
val maxPrice = priceRegex.findAllIn(json).map({
case priceRegex(price) => price.toInt
}).max
println(maxPrice) // print 200
Although Play JSON is handy, you could use Regex as well.
import scala.io.Source
import scala.util.matching.Regex._
val jsonString = Source
.fromFile("jsonData.txt")
.getLines.mkString.split(",")
var maxPrice = 0
jsonString.foreach(each => {
val price: Option[Match] = ("\"price\":(\\d+)").r.findFirstMatchIn(each)
if (price.isDefined) {
maxPrice = Math.max(maxPrice, price.get.group(1).toInt)
}
})

How to parse json with arbitrary schema, update/create one field and write it back as json(Scala)

how do one update/create field in JSON object with arbitrary schema and write it back as JSON in Scala?
I tried with spray-json with something like that:
import spray.json._
import DefaultJsonProtocol._
val jsonAst = """{"anyfield":"1234", "sought_optional_field":5.0}""".parse
val newValue = jsonAst.asJsObject.fields.getOrElse("sought_optional_field", 1)
val newMap = jsonAst.asJsObject.fields + ("sought_optional_field" -> newValue)
JSONObject(newMap).toJson
but it gives weird result: "{"anyfield"[ : "1234", "sought_optional_field" : ]1}
You were almost there :
import spray.json._
import DefaultJsonProtocol._
def changeField(json: String) = {
val jsonAst = JsonParser(json)
val map = jsonAst.asJsObject.fields
val sought = map.getOrElse("sought_optional_field", 1.toJson)
map.updated("sought_optional_field", sought).toJson
}
val jsonA = """{"anyfield":"1234", "sought_optional_field":5.0}"""
val jsonB = """{"anyfield":"1234"}"""
changeField(jsonA)
// spray.json.JsValue = {"anyfield":"1234","sought_optional_field":5.0}
changeField(jsonB)
// spray.json.JsValue = {"anyfield":"1234","sought_optional_field":1}
Using Argonaut:
import argonaut._, Argonaut._
def changeField2(json: String) =
json.parseOption.map( parsed =>
parsed.withObject(o =>
o + ("sought_optional_field", o("sought_optional_field").getOrElse(jNumber(1)))
)
)
changeField2(jsonA).map(_.nospaces)
// Option[String] = Some({"anyfield":"1234","sought_optional_field":5})
changeField2(jsonB).map(_.nospaces)
// Option[String] = Some({"anyfield":"1234","sought_optional_field":1})