scalaplayframework-2.0slick-2.0

How to trim an option string in a Slick column mapping when using Play Scala


What is the correct way, and where is the correct place, to trim a value from a fixed character database column when using the Play framework with Slick?

The reason I want to trim the string is that the database schema specifies a character(40) column type rather than a character varying type.

I have this case class:

case class Test(id: Long, trimMe: Option[String])

I have this Slick table relation:

class Tests(tag: Tag) extends Table[Test](tag, "test") {

  def id     = column[Long  ]("test_id", O.PrimaryKey, O.AutoInc)
  def trimMe = column[String]("trim_me"                         )

  def * = (id, trimMe) <> (Test.tupled, Test.unapply _)

}

I have this test class with a JSON mapping:

object TrimTest {

  implicit val testWrite = new Writes[Test] {
    def writes(test: Test) = Json.obj(
      "id"   -> test.id    ,
      "trim" -> test.trimMe
    )}
}

This all works but returns a space-padded string.

I tried a few variations of trimming in the JSON mapper:

object TrimTest {

  implicit val testWrite = new Writes[Test] {
    def writes(test: Test) = Json.obj(
      "id"    -> test.id                              ,
      "trim1" -> test.trimMe.map(_.trim)              ,
      "trim2" -> test.trimMe.fold("") {_.trim}        ,
      "trim3" -> test.trimMe.map(_.trim).getOrElse("")
    )}
}

trim1 above works, but returns null when the optional value is not present.

trim2 does work for all cases, but I have seen it stated in various places that map then getForElse is "more idiomatic Scala" than using fold.

trim3 is just past the limit of my current understanding of Scala and shows my intent but does not compile.

The compilation error in the case of trim3 is:

type mismatch; found : Object required: play.api.libs.json.Json.JsValueWrapper

I can certainly live with using fold, but what is the usual way to do this?


Solution

  • What about getting the value and then trimming?

    scala> val strOpt = Option("Some string not trimmed    ")
    strOpt: Option[String] = Some(Some string not trimmed    )
    
    scala> strOpt.getOrElse("").trim
    res0: String = Some string not trimmed
    
    scala> val strNone = Option(null)
    strNone: Option[Null] = None
    
    scala> strNone.getOrElse("").trim
    res2: String = ""
    

    Edit:

    You have a Test row which is a case class with two fields, id and trimMe, you want to map this class to a JSON but the problem is you also want to trim it a the trimeMe field.

    If you have a Column[String], from the Slick perspective it means a non optional field, but your case class has an optional field, either both (Test and Tests) have an optional trimMe field (that means a nullable column) or they don't (that means that the column is mandatory).

    In the end the case class you have is the representation of a row you have in your database and that's what Slick queries return(*), it should reflect your schema declaration.

    For the trim problem

    In this way wether it's a None or Some no exception is thrown.

    (*) Not always, you can have queries which return columns instead of whole rows.