前言
play 框架返回 json 类型的数据,和 java 的区别较大。之前碰到了 model 转 json 结果为空的情况,这里做下简单的记录,供后来查询使用。
项目中,使用 slick 作为 FRM 框架集成方式。
问题
slick 查询返回的 model 实体类,需要转换为 json。
解决办法
方式一:通过 fastjson 转换。
通过 Alibaba 的 fastjson 来进行转换的话,需要额外使用 @BeanProperty
来注解所有字段,自动生成 get 和 set 方法,并且多个实体类不能放在同一个 class 中。这是由于 fastjson 是使用 java 的 get set 方法来获取字段的, scala 的 get 和 set 方法不同于 java 。
个人觉得使用 @BeanProperty
注解所有字段太麻烦了,寻找是否有一种更加优雅的方式,将 model 转为 json。
方式二:通过 play 自带的 Json 来转换。 推荐方式
首先,需要在返回类的伴生对象中,定义隐式转换。
导包:import play.api.libs.json._
定义隐式转换:
implicit val format: Format[Company] = Json.format[Company]
然后就可以在 controller 中,使用 Ok(Json.toJson(slickService.getCompany(id).get))
进行转换了。
代码详见:
定义 Company model:
import play.api.libs.json._
case class Company(id: Long, businessCode: String, location: String, name: String, significance: String)
object Company {
implicit val format: Format[Company] = Json.format[Company]
}
controller 调用转换:
class SlickController @Inject()(cc: ControllerComponents, slickService: ISlickService) extends AbstractController(cc) {
def getCompany(id: Long) = Action {
/**
* 返回 json 类型的数据。
* 两种方式:
* 方式一: 手动实现 隐式转换 writes[Company]
* 方式二: 在伴生对象中,添加 对应的 format ,然后调用 Json.toJson 转换 推荐方式
*/
Ok(Json.toJson(slickService.getCompany(id).get))
}
/**
* json data format
* {
"id": 5,
"businessCode": "123456-03",
"location": "北京",
"significance": "IMPORTANT",
"name": "百度公司"
}
*/
// 手动实现 writes[Company],要写出类型,不然 idea 不识别
// model to a JsValue
// 使用 Json.obj 和 Json.arr 来实例化
/**
implicit val companyWrites: Writes[Company] = (company: Company) => {
Json.obj(
"id" -> company.id,
"businessCode" -> company.businessCode,
"location" -> company.location,
"significance" -> company.significance,
"name" -> company.name,
)
}
*/
// 另一种写法 原生写法
/**
implicit val companyWrites_v2: Writes[Company] = (
(JsPath \ "id").write[Long]
.and((JsPath \ "businessCode").write[String])
.and((JsPath \ "businessCode").write[String])
.and((JsPath \ "businessCode").write[String])
.and((JsPath \ "name").write[String])
) (unlift(Company.unapply))
*/
// implicit JsValue to a model
implicit val companyReads: Reads[Company] = (
(JsPath \ "id").read[Long] and
(JsPath \ "businessCode").read[String] and
(JsPath \ "location").read[String] and
(JsPath \ "significance").read[String] and
(JsPath \ "name").read[String]
) (Company.apply _)
}
这里,在 slick 中,有一个小坑,由于在 case class 的伴生对象中,手动定义了隐式转换,就不能直接使用 case class 的 tupled 方法了,需要使用 (Company.apply _).tupled
这种语法。
class CompanyTable(tag: Tag) extends Table[Company](tag, "company") {
val id: Rep[Long] = column[Long]("id", O.PrimaryKey, O.AutoInc, O.Unique)
val business_code = column[String]("business_code")
val location = column[String]("location")
val name = column[String]("name")
val significance = column[String]("significance")
// https://scala-slick.org/doc/3.3.3/schemas.html 存在伴生对象时,需要这么限定
override def * : ProvenShape[Company] = (id, business_code, location, name, significance) <> ((Company.apply _).tupled, Company.unapply)
}