JulienD JulienD - 1 month ago 12
JSON Question

Scala Slick: how to serialize the result of a query to a variable table

I would like to provide a select method to a controller that takes a table name as parameter and returns the result of the SELECT query as JSON. So far I have done this:

def specialSelect(tableName: String) = Action.async {
val tq: TableQuery[_] = tableObjectFactory(tableName)
val res: Future[Seq[_]] = db.run(tq.result)
res.map { p:Seq[_] => Ok(p.toJson) }
}


where
tableObjectFactory
takes my table name and returns a
TableQuery
of the specific type:

def tableObjectFactory(tableName: String): TableQuery[_] = {
tableName match {
case "users" => TableQuery[Users]
case "projects" => TableQuery[Projects]
}
}


If fails because there is no JSON serializer defined for a a generic
Seq[_]
(actually
_
should be a
Product with Serializable
, not sure if that helps).

The table name is not known in advance (found in the URL, such as in
"/special_select/<tableName>"
), and I have 120 such tables so I can't just implement it for each table.

Is there a way to serialize any
Seq[_]
, knowing that the
_
is always a Slick row result (e.g. a case class
UsersRow
), whatever the table is?

I have read about Generic DAOs and ActiveSlick, but I am not sure if should go so far.

Answer

Gson worked, thanks @pamu. Add

libraryDependencies += "com.google.code.gson" % "gson" % "2.8.0"

to build.sbt, then this will convert any Seq[_] or rows to a JsArray that can be given as http response:

import com.google.gson.Gson
import play.api.libs.json._

val gson: Gson = new Gson()

def slickToJson(res: Seq[_]): JsArray = {
  JsArray(res.map(gson.toJson).map(Json.parse))
}

// in controller Action.async:
db.run(...).map(p => Ok(slickToJson(p)))

Rows are serialized to String by Gson, then parsed back to JsObjects by Play api, then put in a JsArray. I couldn't find better.