在 Scala 中使用单个类型别名将多个注释应用于 class and/or 字段
Apply multiple annotation to a class and/or field using a single type alias in Scala
看完scala.annotation.meta.field
的用法https://www.scala-lang.org/api/current/scala/annotation/meta/index.html
由于我正在使用 Xstream,因此我目前需要为该字段设置注释 @XStreamAlias("alias")
以更改它们的反序列化方式。为简单起见,我使用类型别名:
import com.thoughtworks.xstream.annotations.XStreamAlias
type xStreamAlias = XStreamAlias @field
@XStreamAlias("a")
case class A(@xStreamAlias("B") b: String)
我在大约 40 类 中使用了它,所以为了简单起见,我也使用了一个包对象。
但是,为了兼容性和简单性,我现在想切换到 Jackson 并保留一个包含 Jackson 和 Xstream 的注释。我想做类似的事情:
import com.fasterxml.jackson.annotation.JsonProperty
import com.thoughtworks.xstream.annotations.XStreamAlias
type alias = XStreamAlias JsonProperty @field
@alias("a")
case class A(@alias("B") b: String)
我希望通过这种方式将 Jackson 和 XStream 合并为一个注释,因为它们将以相同的方式使用。
我的问题是:
- 这种用法看起来正确且可维护吗?
- 如果您看到上面的示例,我将此类注释仅用于字段而不是 类。即使是 类?
也能安全使用注解吗
我从未见过将 2 个注释与类型别名混合使用的用法,在我看来,这似乎是对可维护性和可读性的改进,以集中所选注释的行为。
我已经看到这个问题:Scala Type aliases for annotations但我不确定是否有新的方法可以做到这一点,回答的用户似乎对他们的解决方案不确定。
type xStreamAlias = XStreamAlias @field
的右侧是用 @field
注释的类型 XStreamAlias
。所以 type alias = XStreamAlias JsonProperty @field
不可能是正确的语法。
您可以定义一个 macro annotation,它被扩展时使用适当的(相同的)参数打开 @XStreamAlias
和 @JsonProperty
。您可以定义如何处理 @alias
注释字段和 class 分开的情况。
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("Enable macro annotations")
class alias(name: String) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AliasMacro.impl
}
object AliasMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val name = c.prefix.tree match {
case q"new alias(${nme: String})" => nme
}
val XStreamAlias = tq"_root_.com.thoughtworks.xstream.annotations.XStreamAlias"
val JsonProperty = tq"_root_.com.fasterxml.jackson.annotation.JsonProperty"
val fieldAnnotation = tq"_root_.scala.annotation.meta.field"
annottees match {
// @alias annotates field
case q"$_ val $valTName: $_ = $_" ::
q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" ::
objectOrNil =>
// switch both @XStreamAlias and @JsonProperty on
val paramss1 = paramss.map(_.map {
case q"$paramMods val $paramTName: $paramTpt = $paramExpr" if paramTName == valTName =>
val paramMods1 = paramMods.mapAnnotations(annotations =>
q"new ($XStreamAlias @$fieldAnnotation)($name)" ::
q"new ($JsonProperty @$fieldAnnotation)($name)" ::
annotations
)
q"$paramMods1 val $paramTName: $paramTpt = $paramExpr"
case param => param
})
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss1) extends { ..$earlydefns } with ..$parents { $self => ..$stats }
..$objectOrNil
"""
// @alias annotates class
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" ::
objectOrNil =>
// switch only @XStreamAlias on
val mods1 = mods.mapAnnotations(annotations =>
q"new $XStreamAlias($name)" :: annotations
)
q"""
$mods1 class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }
..$objectOrNil
"""
}
}
}
用法:
@alias("a")
case class A(b0: Int, @alias("B") b: String, b1: Boolean)
//scalac: {
// @new _root_.com.thoughtworks.xstream.annotations.XStreamAlias("a") case class A extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val b0: Int = _;
// @new _root_.com.thoughtworks.xstream.annotations.XStreamAlias @_root_.scala.annotation.meta.field("B") @new _root_.com.fasterxml.jackson.annotation.JsonProperty @_root_.scala.annotation.meta.field("B") <caseaccessor> <paramaccessor> val b: String = _;
// <caseaccessor> <paramaccessor> val b1: Boolean = _;
// def <init>(b0: Int, @new _root_.com.thoughtworks.xstream.annotations.XStreamAlias @_root_.scala.annotation.meta.field("B") @new _root_.com.fasterxml.jackson.annotation.JsonProperty @_root_.scala.annotation.meta.field("B") b: String, b1: Boolean) = {
// super.<init>();
// ()
// }
// };
// ()
//}
看完scala.annotation.meta.field
的用法https://www.scala-lang.org/api/current/scala/annotation/meta/index.html
由于我正在使用 Xstream,因此我目前需要为该字段设置注释 @XStreamAlias("alias")
以更改它们的反序列化方式。为简单起见,我使用类型别名:
import com.thoughtworks.xstream.annotations.XStreamAlias
type xStreamAlias = XStreamAlias @field
@XStreamAlias("a")
case class A(@xStreamAlias("B") b: String)
我在大约 40 类 中使用了它,所以为了简单起见,我也使用了一个包对象。
但是,为了兼容性和简单性,我现在想切换到 Jackson 并保留一个包含 Jackson 和 Xstream 的注释。我想做类似的事情:
import com.fasterxml.jackson.annotation.JsonProperty
import com.thoughtworks.xstream.annotations.XStreamAlias
type alias = XStreamAlias JsonProperty @field
@alias("a")
case class A(@alias("B") b: String)
我希望通过这种方式将 Jackson 和 XStream 合并为一个注释,因为它们将以相同的方式使用。
我的问题是:
- 这种用法看起来正确且可维护吗?
- 如果您看到上面的示例,我将此类注释仅用于字段而不是 类。即使是 类? 也能安全使用注解吗
我从未见过将 2 个注释与类型别名混合使用的用法,在我看来,这似乎是对可维护性和可读性的改进,以集中所选注释的行为。
我已经看到这个问题:Scala Type aliases for annotations但我不确定是否有新的方法可以做到这一点,回答的用户似乎对他们的解决方案不确定。
type xStreamAlias = XStreamAlias @field
的右侧是用 @field
注释的类型 XStreamAlias
。所以 type alias = XStreamAlias JsonProperty @field
不可能是正确的语法。
您可以定义一个 macro annotation,它被扩展时使用适当的(相同的)参数打开 @XStreamAlias
和 @JsonProperty
。您可以定义如何处理 @alias
注释字段和 class 分开的情况。
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("Enable macro annotations")
class alias(name: String) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AliasMacro.impl
}
object AliasMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val name = c.prefix.tree match {
case q"new alias(${nme: String})" => nme
}
val XStreamAlias = tq"_root_.com.thoughtworks.xstream.annotations.XStreamAlias"
val JsonProperty = tq"_root_.com.fasterxml.jackson.annotation.JsonProperty"
val fieldAnnotation = tq"_root_.scala.annotation.meta.field"
annottees match {
// @alias annotates field
case q"$_ val $valTName: $_ = $_" ::
q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" ::
objectOrNil =>
// switch both @XStreamAlias and @JsonProperty on
val paramss1 = paramss.map(_.map {
case q"$paramMods val $paramTName: $paramTpt = $paramExpr" if paramTName == valTName =>
val paramMods1 = paramMods.mapAnnotations(annotations =>
q"new ($XStreamAlias @$fieldAnnotation)($name)" ::
q"new ($JsonProperty @$fieldAnnotation)($name)" ::
annotations
)
q"$paramMods1 val $paramTName: $paramTpt = $paramExpr"
case param => param
})
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss1) extends { ..$earlydefns } with ..$parents { $self => ..$stats }
..$objectOrNil
"""
// @alias annotates class
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" ::
objectOrNil =>
// switch only @XStreamAlias on
val mods1 = mods.mapAnnotations(annotations =>
q"new $XStreamAlias($name)" :: annotations
)
q"""
$mods1 class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }
..$objectOrNil
"""
}
}
}
用法:
@alias("a")
case class A(b0: Int, @alias("B") b: String, b1: Boolean)
//scalac: {
// @new _root_.com.thoughtworks.xstream.annotations.XStreamAlias("a") case class A extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val b0: Int = _;
// @new _root_.com.thoughtworks.xstream.annotations.XStreamAlias @_root_.scala.annotation.meta.field("B") @new _root_.com.fasterxml.jackson.annotation.JsonProperty @_root_.scala.annotation.meta.field("B") <caseaccessor> <paramaccessor> val b: String = _;
// <caseaccessor> <paramaccessor> val b1: Boolean = _;
// def <init>(b0: Int, @new _root_.com.thoughtworks.xstream.annotations.XStreamAlias @_root_.scala.annotation.meta.field("B") @new _root_.com.fasterxml.jackson.annotation.JsonProperty @_root_.scala.annotation.meta.field("B") b: String, b1: Boolean) = {
// super.<init>();
// ()
// }
// };
// ()
//}