I am parsing a json response from a web service which has some fields defined using hyphens. I want to convert these names to mixed case names in my scala case class. I thought to use the camelizeKeys stipulation but it doesn't seem to work. So for example say I have a json response like:
{"offset":0,"total":359,"per-page":20}
to be converted to:
case class Response(offset: Int, total: Int, perPage: Int)
and I do:
parse(str).camelizeKeys.extract[Response]
I get the error:
Ex: org.json4s.package$MappingException: No usable value for perPage
Did not find value which can be converted into int
A work around is to replace all occurrences of the hyphen with an underscore before parsing the text. So one could do the following:
parse(str.replaceAll("([a-z])-([a-z])", "$1_$2")).camelizeKeys.extract[Response]
But this solution is limited as with complicated data structures it may not work; you could end up corrupting the data returned if any field values contains such a pattern. So a more complete solution was to write my own replaceAll function:
def replaceAll(str: String, regex: String)(f: String => String) = {
val pattern = regex.r
val (s, i) = (("", 0) /: pattern.findAllMatchIn(str)) { case ((s, i), m) =>
(s + str.substring(i, m.start) + f(m.toString), m.end)
}
s + str.substring(i)
}
and apply it to my json string thus:
replaceAll(str, "\"[0-9a-z\\-]*\":")(_.replace('-', '_'))
You can replace the hyphens by underscores as below.
def replaceHyphens(value: JValue): JValue = {
value.transformField {
case JField(name, x) => JField(name.replaceAll("-", "_"), x)
}
}
This function is implemented by referring to rewriteJsonAST in json4s
replaceHyphens(parse(str)).camelizeKeys.extract[Response]
Related
I need to render a sorted map by a user-defined type.
SortedMap[X, Seq[Y]]
Json library should render the map as ordered by the X.name
case class X(order: Int, name: String) extends Ordered[X]
Assume I have X(1, "James"), X(2, "Mel"), X(3, "Ashley").
The output map should be
"James" : Seq(Y)
"Mel" : Seq(Y)
"Ashley": Seq(Y)
The inMap is correctly sorted (as viewed by the debugger), but after the rendering, the sorting order(X.order) is lost. Probably due to the toSeq. Any ideas?
implicit val myWrites = new Writes[SortedMap[X, Seq[Y]]] {
def writes(inMap: SortedMap[X, Seq[Y]]): JsValue =
Json.obj(inMap.map {case (s, o) =>
val r: (String, JsValueWrapper) = s.name() -> Json.toJson(o)
r
}.toSeq:_*)
}
So...
I never meet the word "render" used as "convert to"
ordering by key original key in SortedSet is lost after mapping because you change the key type so the result is ordered by a new key type (here: String)
if you want to preserve the order of items in between mapping I would suggest using ListMap
though in your particular case you can do away with Seq of tuples, as at the end of the day, this is what you need to produce
implicit val myWrites: Writes[SortedMap[X, Seq[Y]]] = new Writes[SortedMap[X, Seq[Y]]] {
def writes(inMap: SortedMap[X, Seq[Y]]): JsValue =
Json.obj(inMap.toSeq.map { case (s, o) =>
s.name() -> Json.toJson(o)
}:_*)
}
I am attempting to learn Scala, and I'm trying to parse a JSON file. I have two lines of code:
var jVal:JValue = parse(json);
val totalCount:Int = (jVal \\ "totalCount").asInstanceOf[Int];
However, (jVal \\ "totalCount") returns a JInt instead of an int. If I print it as a string, it looks like "JInt(38)".
How on earth do I convert this to a regular int? My current code throws an exception saying that
net.liftweb.json.JsonAST$JInt cannot be cast to java.lang.Integer
I've scoured the internet, but I can't find any answers. I would really prefer not to manually parse and remove the "JInt()" part of the string just to get it as an integer.
Surely I am missing a simple way to do this?
Since JInt is a case class, a convenient way to extract the value is using an extractor expression, either in a match:
myJValue match {
case JInt(x) => /* do something with x */
case JString(s) => /* do something with s */
/* etc. */
}
or just an assignment statement, when you know what type to expect:
val JInt(totalCount) = (jVal \\ "totalCount")
This will define totalCount to be the value of "totalCount" in your JSON. Note that it will be of type BigInt. If you want to, you can convert your BigInt to an Int with the toInt method. But if your number is too big for an Int, this method will give you a different number instead of an error. So if huge numbers are at all a possibility, you'll want to check first with isValidInt.
You can also get the value using the num field or values method, but in your code that's harder to work with. To use num, you'd have to do a cast of your JValue to JInt. And if you don't cast to JInt, you won't know the type of the result of values.
I got this similar question but it doesn't help me. (Anorm parse float values). And I can honestly say I didn't understand the solution of that question.
I am getting this complie time error:
could not find implicit value for parameter c: anorm.Column[Float]
at
def getInformation(id: Long): List[(Float, Float, Float)] = {
DB.withConnection { implicit con =>
val query = SQL("select principal,interest,value from myTable where userId={id} and status=true").on("id"->id)
val result = query().map { row =>
Tuple3(row[Float]("principal"), row[Float]("inetrest"), row[Float]("value"))
// ^
}.toList
return result
}
}
Maybe a short review of implicits help you. Let's construct a very basic example:
// some class which will be used as implicit (can be anything)
case class SomeImplicitInformation(maybe: Int, with: Int, data: Int)
// lets assume we have a function that requires an implicit
def functionRequiringImplicit(regularParameters: Int)(implicit imp: SomeImplicitInformation) {
// ...
}
// now if you try to call the function without having an implicit in scope
// you would have to pass it explicitly as second parameter list:
functionRequiringImplicit(0)(SomeImplicitInformation(0,0,0))
// instead you can declare an implicit somewhere in your scope:
implicit val imp = SomeImplicitInformation(0,0,0)
// and now you can call:
functionRequiringImplicit(0)
The error you get simply says that anorm.Column[Float] in not in the scope as implicit. You can solve it by adding it implicitly to your scope or pass it explicitly.
More detailed instructions for you: Since the Column companion object only provides an implicit for rowToDouble you simply have to use the code that is linked in your question. To get it to work put it before your result computation. Later you might want to place it in a val in some enclosing scope.
try this...
def getInformation(id: Long): List[(Float, Float, Float)] = {
DB.withConnection { implicit con =>
val query = SQL("select principal,interest,value from myTable where userId={id} and status=true").on("id"->id)
val result = query().map { row =>
Tuple3(row[Float]("principal").asInstanceOf[Float], row[Float]("inetrest").asInstanceOf[Float], row[Float]("value").asInstanceOf[Float])
}.toList
return result
}
}
implicit def rowToFloat: Column[Float] = Column.nonNull { (value, meta) =>
val MetaDataItem(qualified, nullable, clazz) = meta
value match {
case d: Float => Right(d)
case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass + " to Float for column " + qualified))
}
}
Some functions can accept what we call implicit parameters. Such parameters can, under certain conditions, be derived from the context. If these parameters can't be found, then you have to specify them by hand. If you expect a parameter to be used as an implicit one, it must have been declared implicit, for instance this way :
implicit val myVal = ...
It can be done in the current block or in an enclosing one (in the class body, for instance, or even sometimes in the imports)
The error you get seems to be related to this feature. You're using a function that needs a parameter of type anorm.Column[Float]. The argument is defined to be implicit so that an implicit value can be used and your code may be more concise. Unfortunately, you don't seem to have such an implicit value in your code, so it fails.
Latest Anorm (included in Play 2.3) provides more numeric conversion (see details at http://applicius-en.tumblr.com/post/87829484643/anorm-whats-new-play-2-3 & in Play migration notes).
If you have missing converter, you can add an issue on Play github project.
Best
I get the following as a result from using the scala json parse.
import scala.util.parsing.json.JSON._
val j: String = """["this",["a","b",["c","d"]]]"""
val parse_test=parseFull(j)
now from this I get a result of Option[Any]
I can use get to obtain the results (in this case I am not concerned about invalid json format, so this should be safe, right?)
parse_test.get
res26: Any = List(this, List(a, b, List(c, d)))
Now, how should I go about going from this Any to the List that I had expected? I assume I should use pattern matching, but I can't figure it out. Any help would be much appreciated
Here is my solution:
scala> val Some(xs # List(_*)) = parse_test
xs: List[Any] = List(this, List(a, b, List(c, d)))
What you could do is a fold with a pattern match and a cast:
test_result.fold[List[String]](Nil){
case _ :: list :: _ => list.asInstanceOf[List[String]]
case _ => Nil
}
Assuming you're trying to throw out of the first element and that the 2nd element is the list you wanted.
Edit:
Be aware that if the 2nd element isn't a list this cast would cause an exception. It's really horrible dealing with a List[Any] and trying to decode what's in there...
I am trying to figure out the issue, and tried different styles that I have read on Scala, but none of them work. My code is:
....
val str = "(and x y)";
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int )
var b = pos; //position of where in the expression String I am currently in
val temp = expreshHolder; //holder of expressions without parens
var arrayCounter = follow; //just counts to make sure an empty spot in the array is there to put in the strings
if(exp(b) == '(') {
b = b + 1;
while(exp(b) == ' '){b = b + 1} //point of this is to just skip any spaces between paren and start of expression type
if(exp(b) == 'a') {
temp(arrayCounter) = exp(b).toString;
b = b+1;
temp(arrayCounter)+exp(b).toString; b = b+1;
temp(arrayCounter) + exp(b).toString; arrayCounter+=1}
temp;
}
}
val hold: ArrayBuffer[String] = stringParse(str, 0, new ArrayBuffer[String], 0);
for(test <- hold) println(test);
My error is:
Driver.scala:35: error: type mismatch;
found : Unit
required: scala.collection.mutable.ArrayBuffer[String]
ho = stringParse(str, 0, ho, 0);
^one error found
When I add an equals sign after the arguments in the method declaration, like so:
def stringParse ( exp: String, pos: Int, expreshHolder: ArrayBuffer[String], follow: Int ) ={....}
It changes it to "Any". I am confused on how this works. Any ideas? Much appreciated.
Here's a more general answer on how one may approach such problems:
It happens sometimes that you write a function and in your head assume it returns type X, but somewhere down the road the compiler disagrees. This almost always happens when the function has just been written, so while the compiler doesn't give you the actual source (it points to the line where your function is called instead) you normally know that your function's return type is the problem.
If you do not see the type problem straight away, there is the simple trick to explicitly type your function. For example, if you thought your function should have returned Int, but somehow the compiler says it found a Unit, it helps to add : Int to your function. This way, you help the compiler to help you, as it will spot the exact place, where a path in your function returns a non-Int value, which is the actual problem you were looking for in the first place.
You have to add the equals sign if you want to return a value. Now, the reason that your function's return value is Any is that you have 2 control paths, each returning a value of a different type - 1 is when the if's condition is met (and the return value will be temp) and the other is when if's condition isn't (and the return value will be b=b+1, or b after it's incremented).
class Test(condition: Boolean) {
def mixed = condition match {
case true => "Hi"
case false => 100
}
def same = condition match {
case true => List(1,2,3)
case false => List(4,5,6)
}
case class Foo(x: Int)
case class Bar(x: Int)
def parent = condition match {
case true => Foo(1)
case false => Bar(1)
}
}
val test = new Test(true)
test.mixed // type: Any
test.same // type List[Int]
test.parent // type is Product, the case class super type
The compiler will do its best to apply the most specific type it can based on the possible set of result types returned from the conditional (match, if/else, fold, etc.).