I´m trying to convert a Map[String, Any] which Any contains as well sometimes a Map[String, Any] and so on, into Json String format.
I´ve found this external library http://json4s.org/ but amaze me that Scala core does not contain a native library to achieve this.
Any idea?
Regards.
Scala has not built-in feature, but it has amazing sintax... Here you have it, in 8 idiomatic lines:
def toJson(query: Any): String = query match {
case m: Map[String, Any] => s"{${m.map(toJson(_)).mkString(",")}}"
case t: (String, Any) => s""""${t._1}":${toJson(t._2)}"""
case ss: Seq[Any] => s"""[${ss.map(toJson(_)).mkString(",")}]"""
case s: String => s""""$s""""
case null => "null"
case _ => query.toString
}
Well JSON is an interchange format (albeit a popular one at present), but then so is XML etc etc. The core library arguably shouldn't concern itself with dealing with a long list of data formats - at least this is what I understand is the reason around limiting such support in Scala core, as in other languages.
There are quite a few libraries that will help you achieve this.
I currently use Argonaut for all things JSON in Scala projects, unless I am using Play, in which case it also provides pretty good JSON support. Have a look at Argonaut, you can achieve what you want to quite easily with it.
As far as I understood i thought that you are using your custom implementation for Json which is: Map[String, Any]
So here is custom implementation of function that will return String of your map
def show(json: Map[String, Any]) : String = {
def parse(elem: (String, Any)): String = elem match {
case (a: String, b: Map[String, _]) => "\"" + a + "\"" + ":" + show(b) + ""
case (a: String, b: Boolean) => "\"" + a + "\"" + ":" + b.toString
case (a: String, b: Int) => "\"" + a + "\"" + ":" + b.toString
case (a: String, b: Double) => "\"" + a + "\"" + ":" + b.toString
case (a: String, b: String) => "\"" + a + "\"" + ":\"" + b + "\""
}
val assocs = json.map {
case(key, value) => parse((key,value))
}
"{\n" + assocs.mkString(", \n")+ "}"
}
for such Map:
val mapJson = Map("root" ->
Map("field1" -> 1,
"field2" -> Map("field1" -> true,
"field2" -> 2.03),
"field3" -> "Test"))
it will print:
String = {
"root":{
"field1":1,
"field2":{
"field1":true,
"field2":2.03},
"field3":"Test"}}
Related
so I would like to create a string like "[a-to-b]"
def my func
def makeString(xs: List[String], pre : String, sep: String, post: String) : String =xs match{
case Nil => ""
case head::tail => xs.foldLeft(pre)((r,e) => r + sep + e) + post }
scala > makeString(List("a","to","b"), "[","-","]")
but it turned outString = [a-to-b-]
How can I fix it? Thanks
You've already extracted head and tail, you might as well use them for your foldLeft (instead of pre and xs) and put pre and post outside it.
def makeString(xs: List[String], pre : String, sep: String, post: String): String =
xs match {
case Nil => ""
case head::tail => pre + tail.foldLeft(head)((r,e) => r + sep + e) + post
}
Example Scastie
I'm trying to pass a tuple as an argument to function. Unfortunately i can't do this. Can you give me some tips?
val t = Tuple3(3, "abc", 5.5);
def fun(x: (Int, String, Double) = {
x.productIterator.foreach(i => println("Value: " + i));
}
def(t);
There's a missing closing parenthese and you called def(t) instead of fun(t). Note that you don't need to indicate the constructor Tuple3 :
val t = (3, "abc", 5.5);
def fun(x: (Int, String, Double)) = {
x.productIterator.foreach(i => println("Value: " + i));
}
fun(t);
You are missing a bracket after your method declaration. Also you need to run using fun(t).
val t = Tuple3(3, "abc", 5.5)
def fun(x: (Int, String, Double)) = {
x.productIterator.foreach(i => println("Value: " + i))
}
fun(t)
With pattern matching I am extracting attributes from an AST and saving them in a Map[String, Any], because they can be Strings, Integers, Lists etc. Now I want to use the attributes in a case class. For getting the elements I wrote this method:
def getAttr(attr: Map[String, Any], key : String):Any = {
val optElem = attr.get(key) match {
case Some(elem) => elem
case _ => throw new Exception("Required Attribute " + key + " not found")
}
}
Because I always know what type every attribute value is I want to use the value like this:
case class Object1(id: String, name: String)
Object1("o1", getAttr(attrMap, "name").asInstanceOf[String])
But I get the error "scala.runtime.BoxedUnit cannot be cast to java.lang.String"
What am I doing wrong? Or is there a better way to collect and use my attributes?
Your implementation of getAttr has type Unit since you return result of assignment of value to optElem
To fix:
def getAttr(attr: Map[String, Any], key : String):Any = {
attr.get(key) match {
case Some(elem) => elem
case _ => throw new Exception("Required Attribute " + key + " not found")
}
}
As addition to #Nyavro's absolutely correct answer: to avoid calling asInstanceOf every time you use getAttr, you can add type parameter to it:
def getAttr[R](attr: Map[String, Any], key: String): R = {
val optElem = attr.get(key) match {
case Some(elem) => elem
case _ => throw new Exception("Required Attribute " + key + " not found")
}
optElem.asInstanceOf[R]
}
And then just
Object1("o1", getAttr(attrMap, "name"))
given:
val m = Map[String, Int]("a" -> 1, "b" -> 2, "c" -> 3)
m.foreach((key: String, value: Int) => println(">>> key=" + key + ", value=" + value))
why does the compiler complain
error: type mismatch
found : (String, Int) => Unit
required: (String, Int) => ?
I'm not sure about the error, but you can achieve what you want as follows:
m.foreach(p => println(">>> key=" + p._1 + ", value=" + p._2))
That is, foreach takes a function that takes a pair and returns Unit, not a function that takes two arguments: here, p has type (String, Int).
Another way to write it is:
m.foreach { case (key, value) => println(">>> key=" + key + ", value=" + value) }
In this case, the { case ... } block is a partial function.
oops, read the doco wrong, map.foreach expects a function literal with a tuple argument!
so
m.foreach((e: (String, Int)) => println(e._1 + "=" + e._2))
works
You need to patter-match on the Tuple2 argument to assign variables to its subparts key, value. You can do with very few changes:
m.foreach{ case (key: String, value: Int) => println(">>> key=" + key + ", value=" + value)}
The confusing error message is a compiler bug, which should be fixed in 2.9.2:
Excellent question!
Even when explicitly typing the foreach method, it still gives that very unclear compile error. There are ways around it, but I can't understand why this example does not work.
scala> m.foreach[Unit] {(key: String, value: Int) => println(">>> key=" + key + ", value=" + value)}
<console>:16: error: type mismatch;
found : (String, Int) => Unit
required: (String, Int) => Unit
m.foreach[Unit] {(key: String, value: Int) => println(">>> key=" + key + ", value=" + value)}
^
Docs says argument is tuple -> unit, so We can easily do this
Map(1 -> 1, 2 -> 2).foreach(tuple => println(tuple._1 +" " + tuple._2)))
Yet another way:
Map(1 -> 1, 2 -> 2).foreach(((x: Int, y: Int) => ???).tupled)
However it requires explicit type annotations, so I prefer partial functions.
Say I have a following class:
case class Mock(id: Int, pty1: String, pty2: String)
How can I instantiate it from a following map dynamically?
val params = Map("id" → 234, "pty1" → "asdf", "pty2" → "asdf")
LOL ) Found out that I had a solution already implemented in my lib. Requires Scala 2.10.
def instantiate[T <: AnyRef : Manifest](params: Map[String, Any]): T = {
instantiate(Mirror.classToType(manifest[T].erasure), params).asInstanceOf[T]
}
def instantiate(tpe: Mirror.Type, params: Map[String, Any]) = {
val p = constructorParams(tpe, params)
require(
params.size == p.size &&
p.forall(p => params.contains(p.nameString)),
"Params map `" + params + "` doesn't match `" + p + "`"
)
Option(Mirror.typeToJavaClass(tpe).getConstructor(p.map(p => Mirror.typeToJavaClass(p.tpe)): _*))
.getOrElse(throw new RuntimeException("No appropriate constructor of `" + tpe + "` found"))
.newInstance(p.map(p => params(p.nameString).asInstanceOf[Object]): _*)
}
private def constructorParams(tpe: Mirror.Type, params: Map[String, Any]) = {
tpe.members.find(_.isConstructor).get.paramss(0)
}
I don't think you can in a determistic way, since the names of the parameters are not part of the byte code and therefor at run time there is no way to know which String argument comes first and which second.