Scala Json(json4s) parse return None - json

$ "properties": [
{
"name": "Armour",
"values": [
[
"258",
1
]
],
"displayMode": 0
},...]
I have this JSON array.
I use json4s and scala for parse this code.
case class Property(
name: String,
values: Option[Option[(String, Int)]] = None,
displayMode: Int
)
I write case class for this block, but I get "None" when get values...
I need get "258" in this example. What am I doing wrong?

Your Json looks like you have a list of lists under your values property. I guess you want to have something like dictionary which should be with curly brackets instead of just brackets. Other thing is why would you parse that into Option[Option[(String, Int)]]? Try defining that as optional Map[String, Int].

Could this work?
values: List[(String, Int)] = Nil

Related

Is it possible to use something like JsonPath with kotlin JSON parsing

I have a json structure that I need to (sort of) flatten when serializing it into an object. Some of the elements are at the top level and some are in a sub field. In addition, 1 of the fields is an array of space delimited strings that I need to parse and represent as myString.splig(" ")[0]
So, short of a when expression to do the job, can I use something like a jsonpath query to bind to certain fields? I have thought of even doing some kind of 2-pass binding and then merging both instances.
{
"key": "FA-207542",
"fields": {
"customfield_10443": {
"value": "TBD"
},
"customfield_13600": 45,
"customfield_10900": {
"value": "Monitoring/Alerting"
},
"customfield_10471": [
"3-30536161871 (SM-2046076)"
],
"issuetype": {
"name": "Problem Mgmt - Corrective Action"
},
"created": "2022-08-11T04:46:44.000+0000",
"updated": "2022-11-08T22:11:23.000+0000",
"summary": "FA | EJWL-DEV3| ORA-00020: maximum number of processes (10000) exceeded",
"assignee": null
}
}
And, here's the data object I'd like to bind to. I have represented what they should be as jq expressions.
#Serializable
data class MajorIncident constructor(
#SerialName("key")
val id: String, // .key
val created: Instant, // .fields.created
val pillar: String, // .fields.customfield_10443.value
val impactBreadth: String?,
val duration: Duration, // .fields.customfield_13600 as minutes
val detectionSource: String, //.fields.customfield_10900.value
val updated: Instant, // .fields.updated
val assignee: String, // .fields.assignee
// "customfield_10471": [
// "3-30536161871 (SM-2046076)"
// ],
val serviceRequests: List<String>?, // .fields.customfield_10471 | map(split(" ")[0]) -
#SerialName("summary")
val title: String, //.summary
val type: String, // .fields.issuetype.name // what are options?
)
If you're using Kotlinx Serialization, I'm not sure there is any built-in support for jsonpath.
One simple option is to declare your Kotlin model in a way that matches the JSON. If you really want a flattened object, you could convert from the structured model into the flat model from Kotlin.
Another option is to write a custom serializer for your type.

Scala - how to take json as input arguments and parse it?

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`
}

How to get json parent property instead of taken all of same property name in json4s

I use json4s and scala 2.11.12
the exmaple json is:
{ "name": "joe",
"children": [
{
"name": "Mary",
"age": 5
},
{
"name": "Mazy",
"age": 3
}
]
}
when I want to get the name, instead of get parent name "joe", it give me all names of parents and child by(I use json4s library http://json4s.org/)
compact(render(json \\ "name"))
it return me :
res2: String = {"name":"joe","name":"Mary","name":"Mazy"}
I only need {"name":"joe"}
I only need parent name, How to get only parent name?
val json = "..."
import org.json4s._
import org.json4s.native.JsonMethods._
val parent: JValue = json \ "name"
The \ method which with the native implementation of JSON4S will be lift-json based, will look up a field value by name inside a JSON object. Note, your json needs to be a JValue before you can do this, so from a val jsonData: String you need to call val json = Json.parse(jsonData) to get the initial JValue.
The double backslash \\ method will find all children of the JSON that have a given property, so that's why you are getting the entire set of JObject matches back.

Parse JSON array using Scala Argonaut

I'm using Scala & Argonaut, trying to parse the following JSON:
[
{
"name": "apple",
"type": "fruit",
"size": 3
},
{
"name": "jam",
"type": "condiment",
"size": 5
},
{
"name": "beef",
"type": "meat",
"size": 1
}
]
And struggling to work out how to iterate and extract the values into a List[MyType] where MyType will have name, type and size properties.
I will post more specific code soon (i have tried many things), but basically I'm looking to understand how the cursor works, and how to iterate through arrays etc. I have tried using \\ (downArray) to move to the head of the array, then :->- to iterate through the array, then --\ (downField) is not available (at least IntelliJ doesn't think so).
So the question is how do i:
navigate to the array
iterate through the array (and know when I'm done)
extract string, integer etc. values for each field - jdecode[String]? as[String]?
The easiest way to do this is to define a codec for MyType. The compiler will then happily construct a decoder for List[MyType], etc. I'll use a plain class here (not a case class) to make it clear what's happening:
class MyType(val name: String, val tpe: String, val size: Int)
import argonaut._, Argonaut._
implicit def MyTypeCodec: CodecJson[MyType] = codec3(
(name: String, tpe: String, size: Int) => new MyType(name, tpe, size),
(myType: MyType) => (myType.name, myType.tpe, myType.size)
)("name", "type", "size")
codec3 takes two parameter lists. The first has two parameters, which allow you to tell how to create an instance of MyType from a Tuple3 and vice versa. The second parameter list lets you specify the names of the fields.
Now you can just write something like the following (if json is your string):
Parse.decodeValidation[List[MyType]](json)
And you're done.
Since you don't need to encode and are only looking at decoding, you can do as suggested by Travis, but by implementing another implicit: MyTypeDecodeJson
implicit def MyTypeDecodeJson: DecodeJson[MyType] = DecodeJson(
raw => for {
name <- raw.get[String]("name")
type <- raw.get[String]("type")
size <- raw.get[Int]("size")
} yield MyType(name, type, size))
Then to parse your list:
Parse.decodeValidation[List[MyType]](jsonString)
Assuming MyType is a case class, the following works too:
case class MyType(name: String, type: String, size: Int)
object MyType {
implicit val createCodecJson: CodecJson[MyType] = CodecJson.casecodec3(apply, unapply)(
"name",
"type",
"size"
)
}

Scala/Play: JSON serialization issue

I have a simple custom data structure which I use to map the results from the database:
case class Filter(id: Int, table: String, name: String, Type: String, structure: String)
The resulting object type is List[Filter] and if converted to JSON, it should look something like this:
[
{
"id": 1,
"table": "table1",
"name": "name1",
"Type": "type1",
"structure": "structure1"
},
{
"id": 2,
"table": "table2",
"name": "name2",
"Type": "type2",
"structure": "structure2"
}
]
Now when I try to serialize my object into JSON
val result: String = Json.toJson(filters)
I am getting something like
No Json deserializer found for type List[Filter]. Try to implement an implicit Writes or Format for this type.
How do I solve this seemingly simple problem without writing some ridiculous amount of boilerplate?
My stack is Play 2.2.1, Scala 2.10.3, Java 8 64bit
Short answer:
Just add:
implicit val filterWrites = Json.writes[Filter]
Longer answer:
If you look at the definition of Json.toJson, you will see that its complete signature is:
def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)
Writes[T] knows how to take a T and transform it to a JsValue. You will need to have an implicit Writes[Filter] around that knows how to serialize your Filter instance. The good news is that Play's JSON library comes with a macro that can instantiate those Writes[_] for you, so you don't have to write boring code that transforms your case class's fields into JSON values. To invoke this macro and have its value picked up by implicit search add the line above to your scope.