在 Scala 中使用验证对象构造,在 Anorm 解析器中使用它
Object construction with validation in Scala, using that in an Anorm parser
我有一个简单的案例class金额如下
case class Amount(value: Long, currency: Currency)
以及将字符串货币代码转换为货币对象的伴随对象
object Amount {
private val log = Logger(getClass)
def apply(value: Long, currencyCode: String) : Amount = {
try {
Amount(value, Currency.getInstance(currencyCode))
} catch {
case e: Exception =>
log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
}
调用:
val amount : Amount = Amount(1234, "USD")
当我从数据库中读取一些数据时,我有一个自定义解析器,例如
implicit val amountParser = Macro.parser[Amount]("value", "currencyCode")
然而,编译器报错
scala.ScalaReflectionException: value apply encapsulates multiple overloaded alternatives and cannot be treated as a method. Consider invoking `<offending symbol>.asTerm.alternatives` and manually picking the required method
[error] at scala.reflect.api.Symbols$SymbolApi$class.asMethod(Symbols.scala:228)
[error] at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethod(Symbols.scala:84)
[error] at anorm.Macro$.parserImpl(Macro.scala:70)
[error] at anorm.Macro$.namedParserImpl_(Macro.scala:25)
[error] implicit val amountParser = Macro.parser[Amount]("value", "currencyCode")
我该如何进行这项工作?
更新
在理解了@MikeAllen 的回复后,我决定保留 case class Amount
和 object Amount
原样,而是我为 Amount 编写了一个自定义解析器,如下所示
implicit private val amountParser = for {
value <- long("value")
currencyCode <- str("currency_code")
} yield {
Amount(value, currencyCode)
}
Scala 编译器会自动生成一个 Amount.apply
工厂方法来创建 case class
实例,这就是为什么你会收到这个错误 - 因为你有多个 Amount.apply
方法。其中一个采用 (Long
, Currency
) 类型的参数,另一个采用 (Long
, String
) 类型的参数。错误消息表明您需要 select 从通过 反射 .
报告的重载替代方案中选择其中之一
或者,您的案例 class 和同伴可能会按如下方式重新处理:
final case class Amount(value: Long, currencyCode: String) {
/** Currency. Will create an exception on instantiation if code is invalid. */
val currency: Currency = {
try {
Currency.getInstance(currencyCode)
}
catch {
case e: Exception =>
Amount.log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
}
object Amount {
private val log = Logger(getClass)
}
诚然,这不是很优雅,因为您现在有一个字段 currency
,它不是案例 class 的参数之一,并且不适用于模式匹配,同时还携带字符串形式。
更好的解决方案是在创建 Amount
实例之前,保留原始 case class
并将货币代码字段从 String
转换为 Currency
,作为解析器的一部分:
val amountMapping = {
get[Long]("value") ~ get[String]("currencyCode") map {
case value ~ currencyCode => {
val currency = {
try {
Currency.getInstance(currencyCode)
}
catch {
case e: Exception =>
Amount.log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
Amount(value, currency)
}
}
}
然后您可以使用它来解析行,例如:
def amounts(): List[Amount] = SQL("select * from amounts").as(amountMapping *)
我有一个简单的案例class金额如下
case class Amount(value: Long, currency: Currency)
以及将字符串货币代码转换为货币对象的伴随对象
object Amount {
private val log = Logger(getClass)
def apply(value: Long, currencyCode: String) : Amount = {
try {
Amount(value, Currency.getInstance(currencyCode))
} catch {
case e: Exception =>
log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
}
调用:
val amount : Amount = Amount(1234, "USD")
当我从数据库中读取一些数据时,我有一个自定义解析器,例如
implicit val amountParser = Macro.parser[Amount]("value", "currencyCode")
然而,编译器报错
scala.ScalaReflectionException: value apply encapsulates multiple overloaded alternatives and cannot be treated as a method. Consider invoking `<offending symbol>.asTerm.alternatives` and manually picking the required method
[error] at scala.reflect.api.Symbols$SymbolApi$class.asMethod(Symbols.scala:228)
[error] at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethod(Symbols.scala:84)
[error] at anorm.Macro$.parserImpl(Macro.scala:70)
[error] at anorm.Macro$.namedParserImpl_(Macro.scala:25)
[error] implicit val amountParser = Macro.parser[Amount]("value", "currencyCode")
我该如何进行这项工作?
更新
在理解了@MikeAllen 的回复后,我决定保留 case class Amount
和 object Amount
原样,而是我为 Amount 编写了一个自定义解析器,如下所示
implicit private val amountParser = for {
value <- long("value")
currencyCode <- str("currency_code")
} yield {
Amount(value, currencyCode)
}
Scala 编译器会自动生成一个 Amount.apply
工厂方法来创建 case class
实例,这就是为什么你会收到这个错误 - 因为你有多个 Amount.apply
方法。其中一个采用 (Long
, Currency
) 类型的参数,另一个采用 (Long
, String
) 类型的参数。错误消息表明您需要 select 从通过 反射 .
或者,您的案例 class 和同伴可能会按如下方式重新处理:
final case class Amount(value: Long, currencyCode: String) {
/** Currency. Will create an exception on instantiation if code is invalid. */
val currency: Currency = {
try {
Currency.getInstance(currencyCode)
}
catch {
case e: Exception =>
Amount.log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
}
object Amount {
private val log = Logger(getClass)
}
诚然,这不是很优雅,因为您现在有一个字段 currency
,它不是案例 class 的参数之一,并且不适用于模式匹配,同时还携带字符串形式。
更好的解决方案是在创建 Amount
实例之前,保留原始 case class
并将货币代码字段从 String
转换为 Currency
,作为解析器的一部分:
val amountMapping = {
get[Long]("value") ~ get[String]("currencyCode") map {
case value ~ currencyCode => {
val currency = {
try {
Currency.getInstance(currencyCode)
}
catch {
case e: Exception =>
Amount.log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
Amount(value, currency)
}
}
}
然后您可以使用它来解析行,例如:
def amounts(): List[Amount] = SQL("select * from amounts").as(amountMapping *)