Scala 中值类的隐式 Json 格式化程序

发布于 2025-01-13 02:04:37 字数 687 浏览 4 评论 0原文

我有许多值类组成了一个更大的对象案例类。

final case class TopLevel(
  foo: Foo,
  bar: Bar
)

final case class Foo(foo: String) extends AnyVal

final case class Bar(bar: String) extends AnyVal

object Foo {
  implicit val format = Json.valueFormat[Foo]
}

object Bar {
  implicit val format = Json.valueFormat[Bar]
}

object TopLevel {
  implicit val TopLevelFormat: OFormat[TopLevel] = Json.format[TopLevel]
}

这工作得很好,但是当顶级案例类有很多参数时,构造对象很快就会堆积起来。我试图隐式地执行此操作,例如

object Format {
  implicit def jsonFormatter[A](): Format[A] = Json.valueFormat[A]
}

但我得到

scala.ScalaReflectionException: <none> is not a method

I have many value classes that make up a larger object case class.

final case class TopLevel(
  foo: Foo,
  bar: Bar
)

final case class Foo(foo: String) extends AnyVal

final case class Bar(bar: String) extends AnyVal

object Foo {
  implicit val format = Json.valueFormat[Foo]
}

object Bar {
  implicit val format = Json.valueFormat[Bar]
}

object TopLevel {
  implicit val TopLevelFormat: OFormat[TopLevel] = Json.format[TopLevel]
}

This works fine, but when the top level case class has many arguments the construction objects soon pile up. I tried to do this implicitly, e.g.

object Format {
  implicit def jsonFormatter[A](): Format[A] = Json.valueFormat[A]
}

But I get

scala.ScalaReflectionException: <none> is not a method

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

笔芯 2025-01-20 02:04:37

formatvalueFormat 是宏(实际上不是宏本身,而是它们调用宏)。所以你不能在你想要的地方打电话给他们。 Json.valueFormat[A] 正在尝试扩展,其中 A 还不是值类,而是抽象类型(类型参数)。如果您想推迟宏的扩展,您可以使隐式隐式宏

// in a different subproject
import play.api.libs.json.{Format, OFormat}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

object Format {
  implicit def valueFormat[A]: Format[A] = macro MacroImpl.valueFormatImpl[A]
  implicit def format[A]: OFormat[A] = macro MacroImpl.formatImpl[A]
}

class MacroImpl(val c: whitebox.Context) {
  import c.universe._
  val json = q"_root_.play.api.libs.json.Json"
  def valueFormatImpl[A: WeakTypeTag]: Tree = q"$json.valueFormat[${weakTypeOf[A]}]"
  def formatImpl[A: WeakTypeTag]: Tree = q"$json.format[${weakTypeOf[A]}]"
}

现在,您不需要在类或值类的伴生对象中定义隐式。您只需将隐式从 Format 导入到需要它们的位置

final case class TopLevel(foo: Foo, bar: Bar)
final case class Foo(foo: String) extends AnyVal
final case class Bar(bar: String) extends AnyVal

import Format._
implicitly[Format[Foo]].writes(Foo("a")) // {"foo":"a"}
implicitly[Format[Bar]].writes(Bar("b")) // {"bar":"b"}
implicitly[Format[TopLevel]].writes(TopLevel(Foo("a"), Bar("b"))) // {"foo":{"foo":"a"},"bar":{"bar":"b"}}
implicitly[OFormat[Foo]].writes(Foo("a")) // {"foo":"a"}
implicitly[OFormat[Bar]].writes(Bar("b")) // {"bar":"b"}
implicitly[OFormat[TopLevel]].writes(TopLevel(Foo("a"), Bar("b"))) // {"foo":{"foo":"a"},"bar":{"bar":"b"}}

或者,您可以定义 类或伴生对象的宏注释。它将在伴生对象内生成必要的隐式。但我想现在隐式宏比宏注释要容易一些。

format and valueFormat are macros (well, actually not themselves but they call macros). So you can't call them where you want. Json.valueFormat[A] is trying to expand where A is not a value class yet but an abstract type (a type parameter). If you'd like to postpone expansion of the macros you can make your implicits implicit macros

// in a different subproject
import play.api.libs.json.{Format, OFormat}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

object Format {
  implicit def valueFormat[A]: Format[A] = macro MacroImpl.valueFormatImpl[A]
  implicit def format[A]: OFormat[A] = macro MacroImpl.formatImpl[A]
}

class MacroImpl(val c: whitebox.Context) {
  import c.universe._
  val json = q"_root_.play.api.libs.json.Json"
  def valueFormatImpl[A: WeakTypeTag]: Tree = q"$json.valueFormat[${weakTypeOf[A]}]"
  def formatImpl[A: WeakTypeTag]: Tree = q"$json.format[${weakTypeOf[A]}]"
}

Now you don't need to define implicits in companion objects of your classes or value classes. You just need to import implicits from Format to where you need them

final case class TopLevel(foo: Foo, bar: Bar)
final case class Foo(foo: String) extends AnyVal
final case class Bar(bar: String) extends AnyVal

import Format._
implicitly[Format[Foo]].writes(Foo("a")) // {"foo":"a"}
implicitly[Format[Bar]].writes(Bar("b")) // {"bar":"b"}
implicitly[Format[TopLevel]].writes(TopLevel(Foo("a"), Bar("b"))) // {"foo":{"foo":"a"},"bar":{"bar":"b"}}
implicitly[OFormat[Foo]].writes(Foo("a")) // {"foo":"a"}
implicitly[OFormat[Bar]].writes(Bar("b")) // {"bar":"b"}
implicitly[OFormat[TopLevel]].writes(TopLevel(Foo("a"), Bar("b"))) // {"foo":{"foo":"a"},"bar":{"bar":"b"}}

Alternatively, you can define macro annotation for classes or companion objects. It will generate necessary implicits inside companion objects. But I guess now implicit macros are a little easier than macro annotation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文