Search code examples
scalascala-collectionsdowncast

Terse way to downcast container contents


I'm frequently finding myself doing something like:

val json:Map[String,Any] = getJSON(...)

val v = json.get("username")
val uname = if ( v!=null ) v.asInstanceOf[toString] ) else null

whereas what I'd much prefer to write is:

val uname = json.get[String]("username")

but get doesn't take type parameters -- so my code is overly verbose as follows:

val uname = json.get("username").asInstanceOf[String]

How can I simplify access to containers in situations like this? (In the case of JSON-style objects I'm doing this a LOT)


Solution

  • It can be easily achieved using implicits:

    implicit class MapWGet(m: Map[String, Any]) {
      // something like this
      def gett[T](k: String): T = m(k).asInstanceOf[T]
    }
    

    But beware, asInstance on null for value types (Int, Double, etc) produces zero values (but you can easily modify the method to fit your requirements).

    scala> val json: Map[String, Any] = Map("s" -> "String", "i" -> 1, "n" -> null, "d" -> 0.10D)
    json: Map[String,Any] = Map(s -> String, i -> 1, n -> null, d -> 0.1)
    
    scala> json.gett[String]("s")
    res0: String = String
    
    scala> json.gett[String]("n")
    res1: String = null
    
    scala> json.gett[Int]("n")
    res2: Int = 0
    
    scala> json.gett[Double]("d")
    res3: Double = 0.1
    
    scala> json.gett[Int]("i")
    res4: Int = 1