在Scala中进行验证的对象构造,在Anorm解析器中使用它

时间:2018-03-09 20:25:50

标签: scala anorm

我有一个简单的案例类金额如下

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 Amountobject Amount,而不是我为Amount写了一个自定义解析器,如下所示

    implicit private val amountParser = for {
        value <- long("value")
        currencyCode <- str("currency_code")
      } yield { 
           Amount(value, currencyCode) 
      }

1 个答案:

答案 0 :(得分:2)

Scala 编译器将自动生成用于创建Amount.apply实例的case class工厂方法,这就是您收到此错误的原因 - 因为您有多个{{1 }} 方法。其中一个接受类型(Amount.applyLong)的参数,另一个接受类型(CurrencyLong)的参数。错误消息表明您需要从通过 reflection 报告的重载备选项中选择其中一个。

或者,您的案例类和随播广告可能会重新修改如下:

String

不可否认,这不是那么优雅,因为你现在有一个字段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) } ,它不是case类的参数之一,并且不能用于模式匹配,同时还带有字符串形式。

更好的解决方案是保留原始currency并将货币代码字段从case class转换为String,然后再创建Currency实例,作为其中一部分解析器:

Amount

然后,您可以使用它来解析行,例如:

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)
    }
  }
}