Search code examples
jsonscalaanorm

Anorm Pattern Matching and deprecated list method


I'm using PlayFramework with Anorm to build a REST service with JSON data.

Since I don't know all the database columns in advance, then I've used pattern matching for mapping Row into JSON object.

I've used the example from the documentation (Using-Pattern-Matching) as a starting point.

def any2jsValue(v: Any): JsValue = v match {
    case v2: String => JsString(v2)
    case v2: Long => JsNumber(v2)
    case v2: Int => JsNumber(v2)
    case v2: Double => JsNumber(v2)
    case v2: Float => JsNumber(v2)
    case v2: Some[Any] => any2jsValue(v2.get)
    case None => JsNull
    case v2: Any => JsString("MAPPING NOT IMPLEMENTED: " + v2.getClass()     + "  Value: " + v2.toString)
  }

def row2jsObject(row: Row): JsObject = row.asMap.map({
  case (k: String, v: Any) => Json.obj(k -> any2jsValue(v))
}).foldLeft(Json.obj())((r: JsObject, c: JsObject) => r ++ Json.obj(c.keys.head -> c.values.head))

val sql = SQL("SELECT * FROM world.City WHERE name = 'Milano'")

val cities = sql.map({
  case row: Row => row2jsObject(row)
}).list

Json.obj("cities" -> cities)

The code works fine, but the compiler is warning me that:

method list in class SimpleSql is deprecated: Use SQL("...").as(parser.*)

How to do the same work without the deprecated list method?

I've tried both to implement a parser, and to search alternative methods in the API, but without success.


Solution

  • Not to use the deprecated .list, a RowParser must be defined and used with .* combinator; e.g:

    import java.sql.Connection
    import anorm._
    
    trait Country
    case class SmallCountry(name:String) extends Country
    case class BigCountry(name:String) extends Country
    case object France extends Country
    
    val patternParser = RowParser[Country] {
      case Row("France", _) => Success(France)
      case Row(name:String, pop:Int) if (pop > 1000000) => Success(BigCountry(name))
      case Row(name:String, _) => Success(SmallCountry(name))
      case row => Error(TypeDoesNotMatch(s"unexpected: $row"))
    }
    
    def countries(implicit con: Connection): List[Country] =
      SQL("SELECT name,population FROM Country WHERE id = {i}").
        on("i" -> "id").as(patternParser.*)
    

    In your case:

    import anorm.{ RowParser, SqlResult, Success }
    
    def row2jsObject(row: Row): SqlResult[JsObject] = Success(row.asMap.map({
      case (k: String, v: Any) => Json.obj(k -> any2jsValue(v))
    }).foldLeft(Json.obj())((r: JsObject, c: JsObject) => r ++ Json.obj(c.keys.head -> c.values.head)))
    
    val parser: RowParser[JsObject] = RowParser(row2jsObject(_))
    val cities: List[JsObject] =
      SQL("SELECT * FROM world.City WHERE name = 'Milano'").as(parser.*)