使用Play Framework与Mongo Scala Driver:如何解析Document为JSON?
解决Mongo Scala Driver查询结果转Play JSON的问题
嘿,我来帮你搞定这个Mongo Scala Driver查询结果转Play JSON的问题!你现在的代码只是通过subscribe打印查询结果,没法直接把结果返回给前端,咱们得调整一下代码结构,适配Play的异步模型,同时完成Document到JSON的转换。
第一步:把Mongo查询转换成Future集合
Mongo Scala Driver的Observable可以转换成Future,这样更贴合Play框架的异步Action模式。咱们先重构getAllProcess方法,让它返回Future[List[Document]]:
import org.mongodb.scala._ import org.mongodb.scala.model.Filters.equal import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global // 重构后的方法,返回Future包裹的Document列表 def getAllProcess(userId: String): Future[List[Document]] = { getCollection(COMPANY_FORM) .find(equal("userId", userId)) .toList() // 将Observable转为Observable[List[Document]] .toFuture() // 最终转为Future[List[Document]] }
第二步:把Document转换成Play JSON
这里有两种常用方式,你可以根据需求选择:
方式1:直接转换Document为Play JsValue(快速灵活)
Play的Json API可以直接把Map转成JSON,而Document可以通过toMap()方法转为键值对Map,咱们写个转换函数:
import play.api.libs.json._ // 把单个Document转成Play的JsValue private def docToJson(doc: Document): JsValue = { val fieldPairs = doc.toMap.map { case (key, value) => key -> Json.toJson(value) } JsObject(fieldPairs) }
然后在Play的Controller里写Action,返回JSON响应:
import play.api.mvc._ class ProcessController @Inject()(val controllerComponents: ControllerComponents) extends BaseController { def fetchAllProcesses(userId: String) = Action.async { implicit request => getAllProcess(userId) .map { docs => // 把所有Document转成JSON列表,然后返回Ok响应 val jsonList = docs.map(docToJson) Ok(Json.toJson(jsonList)) } .recover { case ex => // 处理查询异常,返回错误信息给前端 InternalServerError(Json.obj("error" -> s"查询失败: ${ex.getMessage}")) } } }
方式2:用Case Class映射(类型安全,推荐)
如果你的Document结构固定,用Case Class来映射会更安全,还能利用Play的自动JSON格式化功能:
首先定义和Document结构匹配的Case Class,以及对应的JSON Format:
import play.api.libs.json._ // 假设你的Process文档有这些字段,根据实际情况调整 case class Process( userId: String, processName: String, createTime: Long, status: String ) // 自动生成JSON的序列化/反序列化规则 object Process { implicit val processFormat: OFormat[Process] = Json.format[Process] }
然后写一个从Document到Process的转换函数:
private def docToProcess(doc: Document): Process = { Process( userId = doc.getString("userId"), processName = doc.getString("processName"), createTime = doc.getLong("createTime"), status = doc.getString("status") ) }
最后在Controller里使用:
def fetchAllProcesses(userId: String) = Action.async { implicit request => getAllProcess(userId) .map { docs => val processes = docs.map(docToProcess) // 直接把Process列表转成JSON Ok(Json.toJson(processes)) } .recover { case ex => InternalServerError(Json.obj("error" -> s"查询失败: ${ex.getMessage}")) } }
为什么不用原来的subscribe?
原来的subscribe是回调式的处理,没法直接和Play的Action.async需要返回Future[Result]的模型结合。转换成Future之后,我们可以更优雅地处理异步结果,同时统一管理异常,把错误信息返回给前端,而不是只在控制台打印。
内容的提问来源于stack exchange,提问作者Azoulay Jason
相关产品推荐
相关产品推荐

