Scala中转换JsObject:如何处理可选字段?
问题分析与解决方案
你的代码主要存在三个核心问题:
- 类型不匹配:
JsString/JsNumber的构造参数需要具体的String/Long类型,但你直接传入了Option[String]/Option[Long],编译会直接报错。 - 多余的
Try块:asOpt方法本身就是安全的,字段不存在或类型错误时只会返回None,不会抛出异常,用Try包裹完全没必要。 - 类型转换错误:
published是Long类型,应该用JsNumber而非JsString来构建对应JSON值。
下面提供两种修正方案:
方案一:手动处理可选字段转换
import play.api.libs.json._ val input = Json.obj( "title" -> "a book", "author" -> Json.obj("name" -> "mr smith"), "metadata" -> Json.obj("published" -> 10983920189403L) ) case class Book(title: Option[String], author: Option[String], published: Option[Long]) def bookConvertor(inputObject: JsObject): JsObject = { // 安全获取所有可选字段 val title = (inputObject \ "title").asOpt[String] val author = (inputObject \ "author" \ "name").asOpt[String] val published = (inputObject \ "metadata" \ "published").asOpt[Long] val book = Book(title, author, published) // 将存在的字段转换为JSON键值对,过滤掉None的字段 val jsonFields = Seq( book.title.map("title" -> JsString(_)), book.author.map("author" -> JsString(_)), book.published.map("published" -> JsNumber(_)) ).flatten JsObject(jsonFields) }
说明
- 用
map对每个Option字段做转换:当字段存在时生成对应的JSON键值对,不存在时返回None。 flatten会过滤掉所有None元素,最终输出的JsObject只包含输入中实际存在的字段。如果需要把不存在的字段保留为null,可以把map改成fold:"title" -> book.title.fold(JsNull)(JsString(_))
方案二:使用Play JSON自动格式化(推荐)
Play JSON支持通过宏自动生成类型的序列化/反序列化规则,代码更简洁且不易出错:
import play.api.libs.json._ val input = Json.obj( "title" -> "a book", "author" -> Json.obj("name" -> "mr smith"), "metadata" -> Json.obj("published" -> 10983920189403L) ) case class Book(title: Option[String], author: Option[String], published: Option[Long]) // 自动生成Book的JSON格式化器 implicit val bookFormat: OFormat[Book] = Json.format[Book] def bookConvertor(inputObject: JsObject): JsObject = { // 安全解析输入JSON为Book实例,再序列化为目标JSON inputObject.validate[Book].fold( _ => JsObject(Seq.empty), // 解析失败时返回空对象,可根据需求调整 book => Json.toJson(book).as[JsObject] ) }
说明
Json.format[Book]宏会根据case class的结构自动生成解析和序列化逻辑,自动处理可选字段的空值情况。validate方法返回JsResult[Book],通过fold分别处理解析成功和失败的场景。
内容的提问来源于stack exchange,提问作者RobotEyes
相关产品推荐
相关产品推荐

