Lift JSON JValue Extract issue - scala

import net.liftweb.json._
import net.liftweb.json.JsonAST._
import net.liftweb.json.Extraction._
import net.liftweb.json.Printer._
implicit val formats = net.liftweb.json.DefaultFormats
val jV = JArray(List(JInt(10),JString("ahem"),JBool(false)))
I am dealing with a situation of mixed types and attempting to convert Jv to a List[Strings] using
jV.extract[List[String]]
The extraction does not work.
Can someone tell me how should I go about doing this

Lift JSON doesn't have a conversion between Strings and JBools defined in the serialisers.
Does the List inside of the Array always have the same shape? If so, then you can do something like:
case class Datum(id: BigInt, comment: String, bool: Boolean)
val data = jv.extract[List[Datum]]
If that won't work for you as there isn't a uniform shape but you still just want a list of Strings, then you can transform the JBools into JStrings before trying to do the extraction:
jv.map({
case JBool(bool) => if (bool) JString("true") else JString("false")
case x => x
}).extract[List[String]]
In general though, I'd encourage you think about why you are discarding the type information here. Quite a lot of Scala's power comes from it's type system so it's better to use it than to lose it by string-typing things here.

Related

Scala: Typesafe macro to extract value of case class field

We have been banging our heads for a while on this but we cannot find a solution.
In our project we would like to write some DSL to migrate some old code in our codebase.
We would like to make a macro that given an instance of a case class gives us the possibility to extract the value in a typesafe manner. In this case it should be possible to declare x of type Int.
case class MyPersonalCaseClass(token: Int, str: String)
val someVariable = MyPersonalCaseClass(123, "SOMESTRING")
val x = Macros.->(someVariable, "token")
Here “token” is a compile-time constant, referring to the field name.
The macro can be declared with something like
def ->[T](value:T,key: String): Any = macro MacrosImpl.arrow[T]
As for our understanding the only way was with whitebox macros, feel free to change the signatures.
def arrow[T: c.WeakTypeTag](c: whitebox.Context)(value: c.Expr[T], key:c.Expr[String]): c.Expr[Any] =
{
import c.universe._
val caseClassType: c.universe.Type = weakTypeOf[T]
???
}
Scala version is “2.12.8”.
The reason we need something like this is we are porting a lot of code from perl and we would like to give the programmers a vagueish idea they are still writing it.
thanks in advance!
Try
import shapeless.LabelledGeneric
import shapeless.record._
LabelledGeneric[MyPersonalCaseClass].to(someVariable).get(Symbol("token")) // 123

Attempting to generate types from other types in Scala

I'm using Slick on a project and for that I require a Slick representation of my rows and then also an in memory representation. I'm going to use a much simpler example here for brevity. Say for example I have both these types:
type RawType =
(String, Int, Boolean)
type RawTypeRep =
(Rep[String], Rep[Int], Rep[Boolean])
Is there a way to generate one from the other so I don't have to update them in lockstep?
Or perhaps generate them both from a case class? I do have a case class representation, but it's actually slightly different from the types I have because when I hydrate the case class I do some mutations which results in type changes.
This seems like a job for shapeless, though I'm not sure exactly the best way to apply it to your case. The best I can think of is not to generate one from the other, but to at least verify at compile time that the two match up:
import shapeless._
import shapeless.poly._
import shapeless.ops.tuple._
object ToRep extends (Id ~> Rep) {
def apply[A](a: A): Rep[A] = ???
}
type CheckRep[A, B] = Mapper.Aux[A, ToRep.type, B]
type RawType = (String, Int, Boolean)
type RawTypeRep = (Rep[String], Rep[Int], Rep[Boolean])
type RawTypeRepWrong = (Rep[String], Rep[String], Rep[Boolean])
implicitly[CheckRep[RawType, RawTypeRep]] // compiles
implicitly[CheckRep[RawType, RawTypeRepWrong]] // compile error
I am guessing there's probably a better way to handle this given a step back to a larger context. Might be worthwhile to skim the documentation for shapeless to see the kinds of things it can do and see if that gives you any idea.

scala eval function in string form

I'm trying to define a DSL that will dictate how to parse CSVs. I would like to define simple functions to transform values as the values are being extracted from CSV. The DSL will defined in a text file.
For example, if the CSV looks like this:
id,name,amt
1,John Smith,$10.00
2,Bob Uncle,$20.00
I would like to define the following function (and please note that I would like to be able to execute arbitrary code) on the amt column
(x: String) => x.replace("$", "")
Is there a way to evaluate the function above and execute it for each of the amt values?
First, please consider that there's probably a better way to do this. For one thing, it seems concerning that your external DSL contains Scala code. Does this really need to be a DSL?
That said, it's possible to evaluate arbitrary Scala using ToolBox:
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val code = """(x: String) => x.replace("$", "")"""
val toolbox = runtimeMirror(getClass.getClassLoader).mkToolBox()
val func = toolbox.eval(toolbox.parse(code)).asInstanceOf[String => String]
println(func("$10.50")) // prints "10.50"

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?

Select between under_score and camelCase format with json4s

How can I map a json with underscore to a camelCase field in a case class?
import org.json4s.jackson.JsonMethods.parse
import org.json4s.DefaultFormats
object Testing {
implicit val formats = DefaultFormats.withBigDecimal
def test = {
val json = parse("""{"some_field":"a value"}""")
json.extract[ThingDTO]
}
}
case class ThingDTO(someField:String)
The error I get:
No usable value for someField Did not find value which can be
converted into java.lang.String
It doesn't seem to be documented (or at least I missed it when I was looking for it), but there is now a camelizeCase method which you can use on the parsed Json. I stumbled across it in the source code, gave it a go with some snake case Json I was working with and lo and behold, got camelised key names.
So for anyone coming across this question a year on, changing the OP's code to the following will work:
import org.json4s._
import org.json4s.DefaultFormats
import org.json4s.native.JsonMethods._
object Testing {
implicit val formats = DefaultFormats.withBigDecimal
def test = {
val json = parse("""{"some_field":"a value"}""").camelizeKeys
json.extract[ThingDTO]
}
}
case class ThingDTO(someField:String)
Currently, I think that the only option is use back ticks or Transform Function. See it at http://json4s.org/ in Extracting values section.
Best regards