如何覆盖 JS 对象的 toString() 方法以使用 JSON.stringify()?

How to override the toString() method on JS objects to use JSON.stringify()?

我厌倦了在处理我的DTO对象时写blah: "${JSON.stringify(target)}",我只想写blah: "$target"

我的 DTO 看起来像:

@js.native
trait AuthConnectionDetails extends js.Object {
  def clientId: String = js.native
  def accountHostname: String = js.native
}

这些 DTO 用于解析某些 REST API 调用的内容,例如:

val response = js.JSON.parse(xmlHttpRequest.responseText).
  asInstanceOf[AuthConnectionDetails]

我不介意更改我定义 DTO 对象的方式来执行此操作(也许我应该为我的 DTO 或其他东西使用 case 类,而不是原生 js 特征?),但我可以'不知道该怎么做。

我尝试编写一个我可以混合的特征,但没有用,我尝试编写一个隐式扩展方法,但也没有用。

我的隐式代码似乎不适用于 toString:

object JsonToString {
  implicit class JsObjectExtensions(val target: js.Object) extends AnyVal {
    override def toString:String = JSON.stringify(target)
    def json:String = JSON.stringify(target)
  }
}

所以我可以 blah: "${target.json}",这更好 - 但我特别想去掉那些牙套。

有什么方法可以用 scala.js 做到这一点吗?

不,没有办法做到这一点。这是因为字符串插值将始终使用对象本身的toString()方法,无论其类型或隐含的声明是什么类(这通常是 Scala 的事情)。

实现此目的的唯一方法是在每次创建对象时使用自定义 toString() 方法修补它们来实际修改对象。这将包括当您从 JSON 字符串解析它们时。我很确定这比在将它们字符串化时调用 .json 更糟糕。

如果你真的想要,你可以编写你的自定义字符串插值器:

implicit class JsonHelper(private val sc: StringContext) extends AnyVal {
  def dejson(args: Any*): JSONObject = {
    sc.checkLengths(args)
    s(args.map(jsonify))
  }

  private def jsonify(arg: Any) = arg match {
    case obj: js.Object => JSON.stringify(obj)
    case _ => arg.toString
  }
}

您现在可以像这样使用它:

dejson"hello: $target, world: $target2"