Kotlin serialization module. How to serialize/deserialize a plain json field? - json

I have an object with the following structure:
enum class GeometryType { ... }
data class GeometryItem(
val type: GeometryType,
val geomtryJson: String
)
Where an example json could be:
{
type: "Point",
geometryJson: [12.0, 11.0]
}
So gemetryJson is a valid json string, but not quoted.
Could this be accomplished using Kotlin Serialization module?

According to JSON RFC:
A JSON value MUST be an object, array, number, or string, or one of
the following three literal names: false null true.
The representation of strings is similar to conventions used in the C
family of programming languages. A string begins and ends with
quotation marks.
An array structure is represented as square brackets surrounding
zero or more values (or elements). Elements are separated by
commas.
So the value of geometryJson: [12.0, 11.0] has array type, not a string type.
As for kotlinx-serialization out of the box it won't handle such deserialization. You can try to write your own decoder, but I would suggest to use Jackson library for this task. Not a full solution:
val mapper = jacksonObjectMapper()
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
val json ="""
{
type: "POINT",
geometryJson: "[12.0, 11.0]"
}
""".trimIndent()
val geometryItem = mapper.readValue<GeometryItem>(json)
println("geometryItem = $geometryItem")
With such config you can handle at least unquoted json fields.
As for reading array values I would suggest to read it to array of doubles, and if you need to represent it as string use such conversion:
val stringArray = doubleArray.map { it.toString() }.toTypedArray().contentToString()

Related

Flutter: Convert string to Map

I am using SQFlite to store data locally, I have a table in which I have a field called 'json', this field is of type TEXT and stores a json converted to String, such as: '{name: Eduardo, Age: 23, Sex: male}'.
Up to this point, everything works fine.
But then I need to consult this information from the database and how it is stored in a text type format flutter recognizes it as a string. I don't know how to convert it back to an object.
I know that I can build a function to solve this, in the case that the information stored in the json always complies with the same structure. But in my case, the information contained by the json will be variable.
Is there a way to solve this problem?
you can Simply use json.decode function from the dart:convert package.
example:
import 'dart:convert';
main() {
final jsonString = '{"key1": 1, "key2": "hello"}';
final decodedMap = json.decode(jsonString);
// we can now use the decodedMap as a normal map
print(decodedMap['key1']);
}
check those links for more details
https://api.dart.dev/stable/2.10.3/dart-convert/json-constant.html
https://api.dart.dev/stable/2.4.0/dart-convert/dart-convert-library.html
if you have issue that your json keys doesn't have quotes then try this code it transform the unquoted String to a quoted String and then decode it , it's 100% working
final string1 = '{name : "Eduardo", numbers : [12, 23], country: us }';
// remove all quotes from the string values
final string2=string1.replaceAll("\"", "");
// now we add quotes to both keys and Strings values
final quotedString = string2.replaceAllMapped(RegExp(r'\b\w+\b'), (match) {
return '"${match.group(0)}"';
});
// decoding it as a normal json
final decoded = json.decode(quotedString);
print(decoded);
#EduardoColon If the keys are not quoted, then it's not JSON, it's your own custom key-value format. JSON keys have to be quoted. See https://json.org for the "official" JSON syntax.

Play Json Parser: How to ignore field during json parser

I am using Scala with play JSON library for parsing JSON. We are the facing the problem using JSON parsing is, we have same JSON structure, but some JSON files contain different with values structure with the same key name. Let's take an example:
json-1
{
"id": "123456",
"name": "james",
"company": {
"name": "knoldus"
}
}
json-2
{
"id": "123456",
"name": "james",
"company": [
"knoldus"
]
}
my case classes
case class Company(name: String)
object Company{
implicit val _ = Json.format[Company]
}
case class User(id: String, name: String, company: Company)
object User{
implicit val _ = Json.format[Company]
}
while JSON contains company with JSON document, we are getting successfully parsing, but if company contains an array, we are getting parsing exception. Our requirements, are is there anyway, we can use play JSON library and ignore the fields if getting parsing error rather that, ignore whole JSON file. If I am getting, company array values, ignore company field and parse rest of them and map corresponding case class.
I would do a pre-parse function that will rename the 'bad' company.
See the tutorial for inspiration: Traversing-a-JsValue-structure
So your parsing will work, with this little change:
case class User(id: String, name: String, company: Option[Company])
The company needs to be an Option.
Final we found the answer to resolving this issue, as we know, we have different company structure within JSON, so what we need to do, we need to declare company as a JsValue because in any case, whatever the company structure is, it is easily assigned to JsValue type. After that, our requirements are, we need to use object structure, and if JSON contains array structure, ignore it. After that, we used pattern matching with our company JsValue type and one basis of success and failure, we parse or JSON. The solution with code is given below:
case class Company(name: String)
object Company{
implicit val _ = Json.format[Company]
}
case class User(id: String, name: String, company: JsValue)
object User{
implicit val _ = Json.format[Company]
}
Json.parse("{ --- whatevery json--string --- }").validate[User].asOpt match {
case Some(obj: JsObject) => obj.as[Company]
case _ => Company("no-name")
}

How to extract the numbers from json path using scala

My response will be like:
{
"code" : 201,
"message" : "Your Quote Id is 353541551"
}
From this above response I have to extract the number 353541551 alone, so I tried with the basic scala snippets. I tried the following:
.check((status is 201),(jsonPath("Your Quote Id is(//d{9})").saveAs("quoteid")))
And another snippet:
.check((status is 201),(jsonPath("$.message.([0-9]+)").saveAs("quoteid")))
But both didn't work
Please help me out to extract the numbers
Try to use spary json; https://github.com/spray/spray-json
You can define json data structure using scala case class and define custom apply/unapply methods if required.
case class JsonResponse(code: Int, message: String)
object JsonResponse { implicit val f = JsonFormat2(JsonResponse.apply)}
you can use scala Regex to pattern match the string to extract the required value.

Abstraction to extract data from JSON in Scala

I am looking for a good abstraction to extract data form JSON (I am using json4s now).
Suppose I have a case class A and data in JSON format.
case class A(a1: String, a2: String, a3: String)
{"a1":"xxx", "a2": "yyy", "a3": "zzz"}
I need a function to extract the JSON data and return A with these data as follows:
val a: JValue => A = ...
I do not want to write the function a from scratch. I would rather compose it from primitive functions.
For example, I can write a primitive function to extract string by field name:
val str: (String, JValue) => String = {(fieldName, jval) => ... }
Now I would like to compose the function a: JValue => A from str. Does it make sense ?
Consider use of Play-JSON, which has a composable "Reads" object. If you've ever used ReactiveMongo, it can be used in much the same way. Contrary to some older posts here, it can be used stand-alone, without most of the rest of Play.
It uses the common "implicit translator" (my term) idiom. I found that my favorite deserializing pattern for using it is not highlighted in the docs, though - the pattern they espouse is a lot harder to get right, IMHO. I make heavy use of .as and .asOpt, which are documented on the first linked page above, in the small section "Using JsValue.as/asOpt". When deserializing a JSON object, you can say something like
val person:Person = (someParsedJsonObject \ "aPerson").as[Person]
and as long as you have an implicit Reads[Person] in scope, all just works. There are built-in Reads for all primitive types and many collection types. In many cases, it makes sense to put the Reads and Writes implicit objects in the companion object for, e.g., Person.
I thought json4s had a similar feature, but I could be wrong.
Argonaut is fully functional Scala library.
It allows to encode/decode case classes (JSON codecs).
import argonaut._, Argonaut._
case class Person(name: String, age: Int)
implicit def PersonDecodeJson: DecodeJson[Person]
jdecode2L(Person.apply)("name", "age")
// Codec for Person case class from JSON of form
// { "name": "string", "age": 1 }
It also provides JSON cursor (lenses/monocle) for custom parsing.
implicit def PersonDecodeJson: DecodeJson[Person] =
DecodeJson(c => for {
name <- (c --\ "_name").as[String]
age <- (c --\ "_age").as[String].map(_.toInt)
} yield Person(name, age))
// Decode Person from a JSON with property names different
// from those of the case class, and age passed as string:
// { "_name": "string", "age": "10" }
Parsing result is represented by DecodeResult type that can be composed (.map, .flatMap) and handle error cases.

Deserialize string value by JSON-lib (net.sf.json)

I have discovered problem in JSON-Lib with deserialization of JSON string data in nested objects. Following code in Groovy demonstrates problem, you can test it in Groovy Console:
#Grab('net.sf.json-lib:json-lib:2.3:jdk15')
import net.sf.json.*
def s = '''\
{
"attributes": "'{test:1}'",
"nested": { "attributes": "'{test:1}'" }
}'''
def j = JSONSerializer.toJSON(s)
println j
assert j.attributes == "{test:1}" // OK
assert j.nested.attributes == "{test:1}" // Failed
In the example above both attributes in JSON have the same string value "{test:1}". JSONSerializer deserialize the first one into String value but second one into (maybe) JSONObject. I'm not sure because class of that object returns null.
For different string value (which doesn't look like JSON data, e.g. "blah") it works.
How can I change this behavior?
UPDATE
I have tried to quote string values based on JSON-Lib documentation here (thanks to #GemK for pointing there), but with same result. Quoting string values doesn't work in nested objects :-(