【问题标题】:Json4s custom Serializer doesn't work due to type mismatch由于类型不匹配,Json4s 自定义序列化程序不起作用
【发布时间】:2025-12-22 16:45:11
【问题描述】:

我有一个包含密码字段的案例类。为了安全起见,我需要在转换为 Json 时将其屏蔽。 所以我为此创建了一个自定义序列化程序,如下所示。

  import org.json4s.CustomSerializer
  import org.json4s._
  import scala.runtime.ScalaRunTime
  import org.json4s.jackson.JsonMethods._

  case class UserInfo(
                       userid: Long,
                       username: Option[String],
                       password: Option[String]
                     ) {
    override def toString: String = {
      val ui = this.copy(password = password.map(_ =>  "******"))
      ScalaRunTime._toString(ui)
    }
  }

  case object UserInfoSerializer extends CustomSerializer[UserInfo](format => ({
    case jsonObj: JObject =>
      implicit val formats = DefaultFormats
      jsonObj.extract[UserInfo]
  }, {
    case ui: UserInfo =>
      implicit val formats = DefaultFormats
      Extraction.decompose(ui.copy(password = ui.password.map(_ => "******")))
  }))

  implicit val formats = DefaultFormats + UserInfoSerializer

但是当我尝试通过write(render(ui))val ui = UserInfo(123, Some("anonymous"), Some("xxx")) 转换为Json 字符串时,它总是失败并显示

scala> render(ui)
<console>:22: error: type mismatch;
 found   : UserInfo
 required: org.json4s.JValue
    (which expands to)  org.json4s.JsonAST.JValue
       render(ui)

我必须将其用作render(Extraction.decompose(ui)),或者添加从UserInfoJValue 的隐式转换为implicit def userInfo2JValue(ui: UserInfn) = Extraction.decompose(ui)

使自定义序列化程序作为默认序列化程序工作的正确方法是什么?

【问题讨论】:

标签: scala json4s


【解决方案1】:

方法render() 只是呈现JSON AST,它不知道如何将您的类的实例转换为JValue。看看这个diagram,它说明了使用 Json4s 进行数据转换。长话短说,如果你想将你的类呈现为 JSON 字符串,你可以先将它转换为 JValue,然后像你一样呈现:

 render(Extraction.decompose(ui))

或者您可以走捷径并使用Serialization.write,它在内部执行这两项操作:

 Serialization.write(ui)

无论哪种情况,如果已添加显式格式,它将使用您的自定义序列化程序。

【讨论】:

  • 我只是好奇为什么它适用于 scala 预定义类,例如 val json = List(1, 2, 3); compact(render(json))。我想我可以通过定义我自己的隐式转换函数来实现自定义案例类。不确定这是否是 scala lib 类的工作方式?
  • 发生这种情况是因为来自常见 Scala 类型(如 String => JString、Boolean => JBool 以及容器 List => JArray、Map => JObject 等的一些隐式转换,所以在你的例子中List(1,2,3) 在调用 render() 之前被隐式转换为 JArray(List(JInt(1), JInt(2), JInt(3)))