QueryStringBinder for List[String]

playframework-2.0, scala

Solution

I solved it at last. The code used is:

Under models, file QueryBinders.scala:

package models

import play.api.mvc.{JavascriptLitteral, QueryStringBindable}


/**
 * Created with IntelliJ IDEA.
 * User: pvillega
 * Date: 07/05/12
 * Time: 12:06
 * QueryStringBinders for some data types missing in 2.0.1
 */
//TODO: remove when updating to 2.1
object QueryBinders {

  /**
   * QueryString binder for List
   */
  implicit def bindableList[T: QueryStringBindable] = new QueryStringBindable[List[T]] {
    def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params)))
    def unbind(key: String, values: List[T]) = unbindList(key, values)
  }

  private def bindList[T: QueryStringBindable](key: String, params: Map[String, Seq[String]]): List[T] = {
    for {
      values <- params.get(key).toList
      rawValue <- values
      bound <- implicitly[QueryStringBindable[T]].bind(key, Map(key -> Seq(rawValue)))
      value <- bound.right.toOption
    } yield value
  }

  private def unbindList[T: QueryStringBindable](key: String, values: Iterable[T]): String = {
    (for (value <- values) yield {
      implicitly[QueryStringBindable[T]].unbind(key, value)
    }).mkString("&")
  }

  /**
   * Convert a Scala List[T] to Javascript array
   */
  implicit def litteralOption[T](implicit jsl: JavascriptLitteral[T]) = new JavascriptLitteral[List[T]] {
    def to(value: List[T]) = "[" + value.map { v => jsl.to(v)+"," } +"]"
  }

}

On Build.scala:

 val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here
      lessEntryPoints <<= baseDirectory(_ / "app" / "assets" / "stylesheets" ** "style.less"),
      routesImport += "models.QueryBinders"
    )

This will have to be removed when updating to 2.1, but it works and it avoids all the issues I had trying to update the project to 2.1 Snapshot.

Problem

Using Play 2.0.1 I defined the following route: ``` GET /demo/list controllers.Demos.listDemos(page: Int ?= 0, orderBy: Int ?= 1, nameFilter: String ?= "", versionFilter: Long ?= -1, tagFilter: List[String] ?= Nil) ``` But I get this error on compile: ``` No QueryString binder found for type List[String]. Try to implement an implicit QueryStringBindable for this type. ``` I found the code from Play 2.1-RC (not officially released yet) which would solve the issue: ``` /** * QueryString binder for List */ implicit def bindableList[T: QueryStringBindable] = new QueryStringBindable[List[T]] { def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params))) def unbind(key: String, values: List[T]) = unbindList(key, values) } /** * QueryString binder for java.util.List */ implicit def bindableJavaList[T: QueryStringBindable] = new QueryStringBindable[java.util.List[T]] { def bind(key: String, params: Map[String, Seq[String]]) = Some(Right(bindList[T](key, params).asJava)) def unbind(key: String, values: java.util.List[T]) = unbindList(key, values.asScala) } private def bindList[T: QueryStringBindable](key: String, params: Map[String, Seq[String]]): List[T] = { for { values <- params.get(key).toList rawValue <- values bound <- implicitly[QueryStringBindable[T]].bind(key, Map(key -> Seq(rawValue))) value <- bound.right.toOption } yield value } private def unbindList[T: QueryStringBindable](key: String, values: Iterable[T]): String = { (for (value <- values) yield { implicitly[QueryStringBindable[T]].unbind(key, value) }).mkString("&") } ``` But after a couple of hours trying, Play still doesn't find the code. I've tried using a custom object and implicit conversions to that object,but it still doesn't work. How can I force Play to do the implicit conversion? Update I want to avoid 2.1-SNAPSHOT if possible as I'm concerned about its stability, but I will use it if there is no other way. I tried the solution from 4e6 but it did not work: ``` [warn] :::::::::::::::::::::::::::::::::::::::::::::: [warn] :: UNRESOLVED DEPENDENCIES :: [warn] :::::::::::::::::::::::::::::::::::::::::::::: [warn] :: org.slf4j#slf4j-api;1.6.1: configuration not found in org.slf4j#slf4j-api;1.6.1: 'compile'. It was required from org.hibernate#hibernate-validator;4.2.0.Final compile [warn] :: commons-codec#commons-codec;1.4: configuration not found in commons-codec#commons-codec;1.4: 'compile'. It was required from org.apache.httpcomponents#httpclient;4.1.2 compile [warn] :::::::::::::::::::::::::::::::::::::::::::::: ``` My sbt plugin contains this: ``` // Comment to get more information during initialization logLevel := Level.Warn // The Typesafe repository resolvers ++= Seq( Resolver.url("Typesafe Ivy Snapshots", url("http://repo.typesafe.com/typesafe/ivy-snapshots/"))(Resolver.ivyStylePatterns), "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/", "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/", "DefaultMavenRepository" at "http://repo1.maven.org/maven2/", "JavaNet1Repository" at "http://download.java.net/maven/1/") // Use the Play sbt plugin for Play projects addSbtPlugin("play" % "sbt-plugin" % "2.1-SNAPSHOT") ```

Original source