Regarding FoldLeft with list and map in scala - scala

I have a list of strings as json messages and i need to write to object from json using json4s.After parsing i need to store the object and objects attribute as key in a Map.
Using var approach i am able to do that .But i do not want to use var and would like to use foldLeft to do the same.
Definition of class:
case class Definition
{
name:String,
status:String
}
sample json:
{"name":"test",
"status":"Enabled"
}
Using json4s read api i am getting the object as below.
Definition(test,Enabled)
i will get lit objects like above.From this list i need to create a map and need to store object.name as key and object as value.
Looking for optimized approach.

// define case class
case class Definition(name: String, status: String)
// read objects from json
// i use test data to simplify example
val list = List(
Definition("name1", "status1"),
Definition("name2", "status2"),
Definition("name3", "status3")
)
// transform list to map
list.map(d => d.name -> d).toMap
To convert from json you can use:
val objectList = definitionList
.foldLeft(List.empty[Definition])((listObject,result) => listObject :+ read[Definition](result))
or:
definitionList.map(read[Definition])

Related

How do I retrieve a value from a JSON string using gatling JsonPath.query?

Given a string:
val json = """{"id":"derp"}"""
When I try to retrieve the value of ID using JsonPath I get the empty iterator
import io.gatling.jsonpath.JsonPath
JsonPath.query("$.id", json).right.get
// Iterator[Any] = empty iterator
How do I get the value of id?
Although the type of the second arg to JsonPath.query is Any, this function does not seem to support multiple types of arguments. It does not parse the input string, but expects it already to be parsed. Looks a bit weird as design choice.
So, supposing that Jackson is used as the parser lib, the following will work and select the value under id key:
val json = """{"id":"derp"}"""
val parsed = new ObjectMapper().readValue(json, classOf[Object])
// to retrieve the ids as a list:
val ids = JsonPath.query("$.id", parsed).right.get.toList
// the only "id" result of the query
val id = ids.head

How to use Argonaut to decode poorly structured JSON where key name is meaningful

Hi the Decode Person example in the documentation is great if the JSON has a key and value and you can use the key name to extract its value, but what about if the string that makes up the key is arbitrary but meaningful.
for Fxample one open cryptocurrency api can give historic prices of coins and the structure of the JSON returned is different depending on the base currency of the coin I'm asking for and the various quote currencies I want it priced in.. for example lets say I want the price at a particular date of 'DOGE' in 'AUD' and 'XRP' the returned JSON looks like
{"DOGE":{"AUD":0.008835,"XRP":0.004988}}
I can't navigate to base and get its value and then prices and get them as the JSON is not stuctured that way, I need to look for 'DOGE' as a Key then in the Object retrned know that there will be a 'AUD' key and 'XRP' key. And of course that will be different for every result depending on my query.
Of course I know these keys as I create the search based on them but how can I use Argonaut to parse this JSON? Can I somehow create a Decode that closes over my key names?
Any help or guidance is appreciated, thanks.
Since you don't know what the property names are going to be ahead of time, you can't create a codec and decode the raw JSON directly to a Scala class.
You want to parse the raw JSON as a generic argonaut.Json object, then you can pattern match or use fold to examine the contents. For example:
val rawJson: String = ...
val parsed: Either[String, argonaut.Json] = argonaut.Parse.parse(rawJson)
You can see the methods available on argonaut's Json object by inspecting the source code.
As per Fried Brice's answer I did go down the parse route then mapped the resulting Either to produce my data type see code snippet below, suggestions, improvements welcome.
def parseHistoricPriceJSON(rawJson: String, fromcurrency: Currency, toCurrencies: List[Currency]): Either[String, PricedAsset] = {
import argonaut._, Argonaut._
import monocle.macros.syntax.lens._
val parsed: Either[String, Json] = Parse.parse(rawJson)
val myTocurrs = Currency("XRP") :: toCurrencies
parsed.right.map(outer => {
val cursor = outer.cursor
val ps = for {
toC <- myTocurrs
prices <- cursor.downField(fromcurrency.sym)
price <- prices.downField(toC.sym)
thep <- price.focus.number
} yield (toC, thep.toDouble.get)
PricedAsset(fromcurrency, ps)
})
}
case class Currency(sym: String) extends AnyVal {
def show = sym
}
case class PricedAsset(base:Currency, quotePrices: List[(Currency,Double)])

How to iterate and extract values from a list of objects

I am new to Scala and Spray. I have an input JSON that basically maps to these case classes. I have managed to get the json parsed and this the output I am seeing:
case class First(param1:Boolean,param2:List[Parameter])
case class Parameter(name:String,age:Int)
val jsonCollection = input.convertTo[First]
val first=jsonCollection.map(x=>println(x.sql))
returns me this value
Vector(List(First(true,"ram",32),First(true,"siv",22)))
I am not sure how to iterate and retrieve values for each of the list object.

json4s extractor for partial fields (when we care to preserve the original Json)

When parsing a Json String, there are cases where we are interested in parsing just a subset of the fields (the ones we know we need) and delay any semantic parsing/mapping of the rest of the fields.
E.g. we want inspect the key in a key-value Event for (say) load-balancing, but at that point in time we don't have enough info to completely parse the values (we assume that some application further down the road will know what to do with the values):
val json = """{ "key": {"a": true, "b": 123}, "value": [1,2,3,4] }"""
case class Key(a: Boolean, b: Int)
case class Event(key: Key, value: ???) // value can be any valid Json
import org.json4s._
import org.json4s.native.JsonMethods._
implicit val formats = DefaultFormats
val parsedJson = parse(json)
parsedJson.extract[Event]
The question is how to represent the fields that we don't yet know (or care) to parse? What do we add as the type for value?
Note: One solution is to change the event to be case class Event(key: Key). This will work, but it will completely ignore the value, which we ideally want to keep so we can dispatch it properly to another service. So this solution will not work for us.
parse() parses JSON into json4s AST and returns JValue.
AFAIK You can not parse JSON partially because parsing to AST includes JSON validation and for that you need to parse the whole JSON string to AST tree.
But you can partially extract from AST. Here you have two options.
First. Make value field a JValue to defer extracting. You can do it later by calling extract() on this JValue instance.
case class Event(key: Key, value: JValue)
val event = parsedJson.extract[Event]
val value = event.value.extract[Seq[Int]]
Second. Split Event to two classes and extract them separately.
case class EventKey(key: Key)
case class EventValue(value: Seq[Int])
val parsedJson = parse(json)
val eventKey = parsedJson.extract[EventKey]
val eventValue = parsedJson.extract[EventValue]

Creating a `Decoder` for arbitrary JSON

I am building a GraphQL endpoint for an API using Finch, Circe and Sangria. The variables that come through in a GraphQL query are basically an arbitrary JSON object (let's assume there's no nesting). So for example, in my test code as Strings, here are two examples:
val variables = List(
"{\n \"foo\": 123\n}",
"{\n \"foo\": \"bar\"\n}"
)
The Sangria API expects a type for these of Map[String, Any].
I've tried a bunch of ways but have so far been unable to write a Decoder for this in Circe. Any help appreciated.
The Sangria API expects a type for these of Map[String, Any]
This is not true. Variables for an execution in sangria can be of an arbitrary type T, the only requirement that you have an instance of InputUnmarshaller[T] type class for it. All marshalling integration libraries provide an instance of InputUnmarshaller for correspondent JSON AST type.
This means that sangria-circe defines InputUnmarshaller[io.circe.Json] and you can import it with import sangria.marshalling.circe._.
Here is a small and self-contained example of how you can use circe Json as a variables:
import io.circe.Json
import sangria.schema._
import sangria.execution._
import sangria.macros._
import sangria.marshalling.circe._
val query =
graphql"""
query ($$foo: Int!, $$bar: Int!) {
add(a: $$foo, b: $$bar)
}
"""
val QueryType = ObjectType("Query", fields[Unit, Unit](
Field("add", IntType,
arguments = Argument("a", IntType) :: Argument("b", IntType) :: Nil,
resolve = c ⇒ c.arg[Int]("a") + c.arg[Int]("b"))))
val schema = Schema(QueryType)
val vars = Json.obj(
"foo" → Json.fromInt(123),
"bar" → Json.fromInt(456))
val result: Future[Json] =
Executor.execute(schema, query, variables = vars)
As you can see in this example, I used io.circe.Json as variables for an execution. The execution would produce following result JSON:
{
"data": {
"add": 579
}
}
Here's a decoder that works.
type GraphQLVariables = Map[String, Any]
val graphQlVariablesDecoder: Decoder[GraphQLVariables] = Decoder.instance { c =>
val variablesString = c.downField("variables").focus.flatMap(_.asString)
val parsedVariables = variablesString.flatMap { str =>
val variablesJsonObject = io.circe.jawn.parse(str).toOption.flatMap(_.asObject)
variablesJsonObject.map(j => j.toMap.transform { (_, value: Json) =>
val transformedValue: Any = value.fold(
(),
bool => bool,
number => number.toDouble,
str => str,
array => array.map(_.toString),
obj => obj.toMap.transform((s: String, json: Json) => json.toString)
)
transformedValue
})
}
parsedVariables match {
case None => left(DecodingFailure(s"Unable to decode GraphQL variables", c.history))
case Some(variables) => right(variables)
}
}
We basically parse the JSON, turn it into a JsonObject, then transform the values within the object fairly simplistically.
Although the above answers work for the specific case of Sangria, I'm interested in the original question: What's the best approach in Circe (which generally assumes that all types are known up front) for dealing with arbitrary chunks of Json?
It's fairly common when encoding/decoding Json that 95% of the Json is specified, but the last 5% is some type of "additional properties" chunk which can be any Json object.
Solutions I've played with:
Encode/Decode the free-form chunk as Map[String,Any]. This means you'll have to introduce implicit encoders/decoders for Map[String, Any], which can be done, but is dangerous as that implicit can be pulled into places you didn't intend.
Encode/Decode the free-form chunk as Map[String, Json]. This is the easiest approach and is supported out of the box in Circe. But now the Json serialization logic has leaked out into your API (often you'll want to keep the Json stuff completely wrapped, so you can swap in other non-json formats later).
Encode/Decode to a String, where the string is required to be a valid Json chunk. At least you haven't locked your API into a specific Json library, but it doesn't feel very nice to have to ask your users to create Json chunks in this manual way.
Create a custom trait hierarchy to hold the data (e.g sealed trait Free; FreeInt(i: Int) extends Free; FreeMap(m: Map[String, Free] extends Free; ...). Now you you can create specific encoders/decoders for it. But what you've really done is replicate the Json type hierarchy that already exists in Circe.
I'm leaning more toward option 3. Since it's the most flexible, and will introduce the least dependencies in the API. But none of them are entirely satisfying. Any other ideas?