Search code examples
jsonscalascala-collectionsanorm

type mismatch; found : scala.collection.immutable.Stream[String] required: String in Play Scala?


I am trying to display json data from database using scala/anorm of Play(2.2.x), If I give the following trial, I am getting the error like: type mismatch; found : scala.collection.immutable.Stream[String] required: String, but If I write like: sql().map(row => rowString).toString - is giving Stream type json data(i don't need it), so how can I get my normal json data for it ? Please help me and thank in advance.

contorller:

class Test extends Controller {
    def getTest = Action { 
    var sql: SqlQuery = SQL("select name::TEXT from test");
    def values: String =  DB.withConnection { implicit connection => 
    sql().map(row => row[String]("name"))//giving error: type mismatch; found : scala.collection.immutable.Stream[String] required: String
    }
    Ok(values)
    }

Solution

  • It looks like you are using Anorm for database access. As noted in the comments above, executing the Anorm query returns a Stream rather than just a value. When you map the value, it is again returning a Stream of String so that the results can be processed as a stream. This is very useful for large result sets where it makes more sense to process the stream incrementally and stream the result to the client. It looks like you are just getting started so you probably don't want to worry about that right now.

    You can covert the result from a stream into a conventional list by simply using toList:

    val resList = sql().map(row => row[String]("name")).toList
    

    Now, you also need to unescape the string and return it as a JSON result. That can be done by using string interpolations. To do this, you just need to surround the String in a StringContext and call the s method with no arguments.

    val newResList = resList.map(s => StringContext(s).s())
    

    Finally, you should actually be converting the String into a PlayJson JsValue type so that your controller is actually returning the right type.

    Json.parse(newResList.mkString("[",",","]")
    

    The mkString method is used to convert the list into a valid JSON string. You will need to import play.api.libs.json.Json for this to work. Passing a JsValue to Ok ensures that the mime type of the response is set to "application/json". You can learn more about using JSON with play here. This will be important if you intend to build JSON services.

    Putting it together, you get the following:

    class Test extends Controller {
      def getTest = Action { 
        var sql: SqlQuery = SQL("select name::TEXT from test");
        def values: JsValue =  DB.withConnection { implicit connection => 
          val resList = sql().map(row => row[String]("name")).toList 
          val newResList = resList.map(s => StringContext(s).s())
          Json.parse(newResList.mkString("[",",","]")
      }
      Ok(values)
    }
    

    You could also use the following alternate structure to simplify and get rid of the rather sloppy mkString call.

    class Test extends Controller {
      def getTest = Action { 
        var sql: SqlQuery = SQL("select name::TEXT from test");
        def values: JsValue =  DB.withConnection { implicit connection => 
          val resList = sql().map(row => row[String]("name")).toList 
          Json.toJson(resList.map(s => Json.parse(StringContext(s).s())))
      }
      Ok(values)
    }
    

    This parses each of the JSON strings in the list and then converts the list into JsArray again using the capabilities of Play's built in JSON library.