假设我有一个scala case类,可以序列化为json(使用json4s或其他一些库):
case class Weather(zip : String, temp : Double, isRaining : Boolean)
如果我使用HOCON配置文件:
allWeather {
BeverlyHills {
zip : 90210
temp : 75.0
isRaining : false
}
Cambridge {
zip : 10013
temp : 32.0
isRainging : true
}
}
有没有办法使用typesafe config自动实例化Weather
对象?
我正在寻找表格
val config : Config = ConfigFactory.parseFile(new java.io.File("weather.conf"))
val bevHills : Weather = config.getObject("allWeather.BeverlyHills").as[Weather]
该解决方案可以利用"allWeather.BeverlyHills"
引用的值是json“blob”的事实。
我显然可以编写自己的解析器:
def configToWeather(config : Config) =
Weather(config.getString("zip"),
config.getDouble("temp"),
config.getBoolean("isRaining"))
val bevHills = configToWeather(config.getConfig("allWeather.BeverlyHills"))
但这似乎不够优雅,因为对天气定义的任何更改都需要更改为configToWeather
。
提前感谢您的审核和回复。
答案 0 :(得分:11)
typesafe配置库有来自config的API to instantiate对象,它使用java bean约定。但据我所知,案例类不遵循这些规则。
有several scala libraries包装typesafe配置并提供您正在寻找的scala特定功能。
例如,使用pureconfig阅读配置可能看起来像
val weather:Try[Weather] = loadConfig[Weather]
其中Weather
是config
答案 1 :(得分:7)
扩展Nazarii的回答,以下内容对我有用:
import scala.beans.BeanProperty
//The @BeanProperty and var are both necessary
case class Weather(@BeanProperty var zip : String,
@BeanProperty var temp : Double,
@BeanProperty var isRaining : Boolean) {
//needed by configfactory to conform to java bean standard
def this() = this("", 0.0, false)
}
import com.typesafe.config.ConfigFactory
val config = ConfigFactory.parseFile(new java.io.File("allWeather.conf"))
import com.typesafe.config.ConfigBeanFactory
val bevHills =
ConfigBeanFactory.create(config.getConfig("allWeather.BeverlyHills"), classOf[Weather])
答案 2 :(得分:0)
另一个选择是将circe.config与以下代码一起使用。参见https://github.com/circe/circe-config
import io.circe.generic.auto._
import io.circe.config.syntax._
def configToWeather(conf: Config): Weather = {
conf.as[Weather]("allWeather.BeverlyHills") match {
case Right(c) => c
case _ => throw new Exception("invalid configuration")
}
}
答案 3 :(得分:0)
一个没有外部库的简单解决方案,灵感来自 playframework Configuration.scala
trait ConfigLoader[A] { self =>
def load(config: Config, path: String = ""): A
def map[B](f: A => B): ConfigLoader[B] = (config, path) => f(self.load(config, path))
}
object ConfigLoader {
def apply[A](f: Config => String => A): ConfigLoader[A] = f(_)(_)
implicit val stringLoader: ConfigLoader[String] = ConfigLoader(_.getString)
implicit val booleanLoader: ConfigLoader[Boolean] = ConfigLoader(_.getBoolean)
implicit val doubleLoader: ConfigLoader[Double] = ConfigLoader(_.getDouble)
}
object Implicits {
implicit class ConfigOps(private val config: Config) extends AnyVal {
def apply[A](path: String)(implicit loader: ConfigLoader[A]): A = loader.load(config, path)
}
implicit def configLoader[A](f: Config => A): ConfigLoader[A] = ConfigLoader(_.getConfig).map(f)
}
用法:
import Implicits._
case class Weather(zip: String, temp: Double, isRaining: Boolean)
object Weather {
implicit val loader: ConfigLoader[Weather] = (c: Config) => Weather(
c("zip"), c("temp"), c("isRaining")
)
}
val config: Config = ???
val bevHills: Weather = config("allWeather.BeverlyHills")