关于Scala:如何使用squeryl和spray-json为Play2中的表实现通用的REST API

How to implement a generic REST api for tables in Play2 with squeryl and spray-json

我正在尝试在Play2中实现一个控制器,该控制器为我的数据库表公开了一个简单的REST风格的api。我正在使用squeryl进行数据库访问,并使用spray-json将对象与json

相互转换。

我的想法是只有一个通用控制器来完成所有工作,因此我在conf/routes中设置了以下路由:

1
2
GET  /:tableName              controllers.Crud.getAll(tableName)
GET  /:tableName/:primaryKey  controllers.Crud.getSingle(tableName, primaryKey)

..和以下控制器:

1
2
3
4
object Crud extends Controller {
    def getAll(tableName: String) = Action {..}
    def getSingle(tableName: String, primaryKey: Long) = Action {..}
}

(是,缺少创建/更新/删除,但让我们先阅读以开始工作)

我已经通过扩展的squeryl \\的Schema

将表映射到案例类

1
2
3
4
object MyDB extends Schema {
    val accountsTable = table[Account]("accounts")
    val customersTable = table[Customer]("customers")
}

我已经将我的案例类告诉了Spray-json,因此它知道如何转换它们。

1
2
3
4
object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val accountFormat = jsonFormat8(Account)
  implicit val customerFormat = jsonFormat4(Customer)    
}

到目前为止,只要我直接使用table-instances,它实际上就可以很好地工作。当我试图使代码通用化时,问题就浮出水面,以至于我最终只能使用一个控制器来访问所有表:我被一些无法编译的代码所卡住,我不确定该怎么办\\的下一步。

spray-json似乎是类型问题,当我尝试在我的getAll函数中将对象列表转换为json时发生。

这是我的一般尝试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def getAll(tableName: String) = Action {

  val json = inTransaction {

    // lookup table based on url
    val table = MyDB.tables.find( t => t.name == tableName).get  

    // execute select all and convert to json
    from(table)(t =>
      select(t)
    ).toList.toJson // causes compile error
  }

  // convert json to string and set correct content type
  Ok(json.compactPrint).as(JSON)
}

编译错误:

1
2
3
4
5
[error] /Users/code/api/app/controllers/Crud.scala:29:
Cannot find JsonWriter or JsonFormat type class for List[_$2]
[error]       ).toList.toJson
[error]                ^
[error] one error found

我猜测问题可能是json库需要在编译时知道我要向其抛出哪种模型类型,但是我不确定(请注意其中的List[_$2]编译错误)。我已尝试对代码进行以下更改以编译并返回结果:

  • 删除通用表查询(MyDB.tables.find(.....).get),而是使用特定的表实例,例如MyDB.accountsTable。证明JSON序列化可以正常工作。但是,这不是通用的,它将需要唯一的控制器和db中每个表的路由配置。
  • 在调用toJson之前,将对象列表从db查询转换为字符串。即:toList.toJson-> toList.toString.toJson。证明表的通用查找有效,但不是正确的json响应,因为它是对象的字符串序列化列表。
  • 有没有人?


    您的猜测是正确的。 MyDb.tables是一个Seq [Table [_]],换句话说,它可以容纳任何类型的表。编译器无法使用find方法找出您找到的表的类型,并且看起来JSON转换需要该类型。有多种方法可以解决此问题,但是您需要对模型类进行某种类型的访问。