将一个case class转换为js.Object的最佳方法是什么?

12

假设我有一个case class:

case class Name(first: String, last: String)
val n1 = Name("John", "Doe")
val n2 = Name("Mary", "Doe")

而我有一个 JavaScript 对象:

@js.native
trait MyJSObject extends js.Object { 
  def add(name: js.Object): Unit = js.native 
}

名字应该符合以下格式

{"first":"John","last":"Doe"}

如何将我的case object转换为js.Object最佳方法是什么?

我知道可以通过upickle.write()将其转换为json字符串来实现,所以我的问题是这是否是最佳方法?


一时之间,我怀疑upickle.write可能是处理它最方便的方式。你可能可以使用Shapeless获得更加优雅的解决方案,但这需要更多的努力,而且没有明显的好处... - Justin du Coeur
那么,如何将JSON字符串转换为JS对象呢? - user79074
我自己没有尝试过,但我猜想js.eval(String)可能会奏效。 - Justin du Coeur
另外,我应该检查一下:你是否在意这些是符合惯例的Scala case类?如果它们的主要目的是为了Scala互操作性,那么你应该使用@ScalaJSDefined -- 这就是它的设计初衷。 - Justin du Coeur
是的,这些是由我的JVM提供的case类,因此我无法在项目的那部分引用Scala注释,据我所知。 - user79074
2个回答

11

如果你控制这个 case class,你只需要在它上面添加 @JSExportAll 注解:

@JSExportAll
case class Name(first: String, last: String)

这将创建名为firstlast的属性。请注意,这不是简单对象。但是,根据add方法的作用,这已经足够了(比转换为JSON并再次解析要轻量得多)。

如果您需要交叉编译案例类,可以在JVM项目中包含scalajs-stubs库。它将提供虚拟注释(不会写入.class文件)。

如果需要一个js.Object,则可以使用导出类型检查器:

val x = Name("foo", "bar")
js.use(x).as[js.Object]

这甚至可以与定义所需字段的trait一起使用:

@js.native
trait Options extends js.Object {
  def first: String = js.native
  def last: String = js.native
}

val x = Name("foo", "bar")
js.use(x).as[Options]

js.use(x).as 调用会失败,如果类型不匹配(即如果 Options 定义了一个 Name 没有定义的字段)。


1
但是我如何将一个Name实例转换为js.Object? - user79074
2
这个解决方案在我的情况下不起作用,因为我的 case class 也在 jvm 端使用。如果我理解正确的话,在那里我不能引用 scala.scalajs 包。 - user79074
1
你能否添加一些关于这种方法的限制信息? 我注意到(截至Scala.js 0.6.22),序列化对象中的字段名称会得到$1$smth后缀。 如果结果对象需要改变字段,则它们必须显式地成为var。 此外,.as[js.Object]不起作用,因为它需要一个特质。 - laughedelic
关于先前评论中的$1$smth后缀,请注意原始名称也被捕获在对象中,如此答案所示。因此,继续使用case class Name(first: String, last: String)示例,除了first$1last$1之外,您还应该获取firstlast属性。 - carueda
在 Scala.js 1.x 中,use 是什么? - steinybot
显示剩余3条评论

-1

使用 uPickle

val jsValue: Js.Value = upickle.Types.writeJs[Name](n1)
val jsObject: js.Any = upickle.json.writeJs(jsValue).asInstanceOf[js.Any]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接