2

I have been using Play framework 2.0 for about 6 months, I have been wondering why they use so many boilerplate code to parse from my SQL query returns, like below:

case class Journal_accountDetail(amount: Double, states: Boolean)

val Journal_AccountParser: RowParser[Journal_accountDetail] = {
    get[Double] ("amount") ~
    get[Boolean] ("states") map({
        case amount~states => Journal_accountDetail(amount,states)
    })
}

Is it something that boost Play framework performance ??

3
  • Can you give an example of library/framework where less boilerplate is used? Commented Jun 24, 2014 at 11:08
  • if I'm not mistaken, Ruby on rails / yii doesn't use some code like that to get or use the SQL query result.. Right ? I'm still a newbie here.. Commented Jun 24, 2014 at 14:20
  • 2
    In one word, for type-safety. Commented Jun 24, 2014 at 15:15

2 Answers 2

13

The parsing API can seem a bit tedious at first, but it's quite powerful when you start combining and re-using the parsers, and much less ugly than pattern matching in every function that returns an SQL result.

Imagine something like this:

case class User(id: Int, name: String, address: Address)
case class Address(id: Int, street: String, city: String, state: State, country: Country)
case class State(id: Int, abbrev: String, name: String)
case class Country(id: Int, code: String, name: String)

To construct the User you need to parse a result with multiple JOINs. Rather than having one large parser, we construct one for each class in it's companion object:

object User {
    val parser: RowParser[User] = {
        get[Int]("users.id") ~
        get[String]("users.name") ~ 
        Address.parser map {
            case id~name~address => User(id, name, address)
        }
    }
}

object Address {
    val parser: RowParser[Address] = {
        get[Int]("address.id") ~
        get[String]("address.street) ~
        get[String]("address.city") ~ 
        State.parser ~
        Country.parser map {
            case id~street~city~state~country => Address(id, street, city, state, country)
        }
    }
}

object State {
    val parser: RowParser[State] = {
        get[Int]("states.id") ~
        get[String]("states.abbrev") ~ 
        get[String]("states.name") map {
            case id~abbrev~name => State(id, abbrev, name)
        }
    }
}

object Country {
    val parser: RowParser[Country] = {
        get[Int]("countries.id") ~
        get[String]("countries.code") ~ 
        get[String]("countries.name") map {
            case id~code~name => Country(id, code, name)
        }
    }
}

Note how I'm using the full table space in the parsers, in order to avoid column name collisions.

Altogether, this looks like a lot of code, but for each source file it's only a small footprint. And the largest benefit is that our User parser is quite clean despite it's complex structure. Let's say in User the address is actually Option[Address]. Then accounting for that change is as simple as changing Address.parser in the User parser to (Address.parser ?).

For parsing simple queries, yes it does seem like a lot of boilerplate. But I'm quite thankful for the parsing API when it comes to parsing examples like the one above (and much more complex ones).

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, for your trick.. But, I'm more concern about performance.. Is it only for better code readability ? I read once about JSON parsing in Play 2.0, there are some boilerplate code too, but some people says that it can help generate Json faster during execution..
There really shouldn't be any noticeable difference in performance. Type-safety and readability should come first. Premature optimization is not the way to go. Your SQL query is more likely to cause performance issues than the parser. I've been using Play for well over a year and haven't had performance issues caused by parsers.
This is great (I think). Can you give an example of an anorm query with a join that would work with your User parser?
4

anorm.SqlParser also provides convinent parser functions, like .str, .int, .float, .double, ... instead of .get[String], .get[Int], .get[Float], .get[Double]. Best regards.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.