Pureconfig 将配置读取为属性映射
Pureconfig read config as properties map
是否可以将 pureconfig 读取属性设置为 Map[String, String]
?我有以下
application.conf
:
cfg{
some.property.name: "value"
some.another.property.name: "another value"
}
这是我尝试读取配置的应用程序:
import pureconfig.generic.auto._
import pureconfig.ConfigSource
import pureconfig.error.ConfigReaderException
object Model extends App {
case class Config(cfg: Map[String, String])
val result = ConfigSource.default
.load[Config]
.left
.map(err => new ConfigReaderException[Config](err))
.toTry
val config = result.get
println(config)
}
问题是它抛出以下异常:
Exception in thread "main" pureconfig.error.ConfigReaderException: Cannot convert configuration to a Model$Config. Failures are:
at 'cfg.some':
- (application.conf @ file:/home/somename/prcfg/target/classes/application.conf: 2-3) Expected type STRING. Found OBJECT instead.
at Model$.$anonfun$result(Model.scala:11)
at scala.util.Either$LeftProjection.map(Either.scala:614)
at Model$.delayedEndpoint$Model(Model.scala:11)
at Model$delayedInit$body.apply(Model.scala:5)
at scala.Function0.apply$mcV$sp(Function0.scala:39)
at scala.Function0.apply$mcV$sp$(Function0.scala:39)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
at scala.App.$anonfun$main(App.scala:73)
at scala.App.$anonfun$main$adapted(App.scala:73)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
at scala.collection.AbstractIterable.foreach(Iterable.scala:920)
at scala.App.main(App.scala:73)
at scala.App.main$(App.scala:71)
at Model$.main(Model.scala:5)
at Model.main(Model.scala)
有办法解决吗?我预计 Map[String, String]
将包含以下映射:
some.property.name -> "value"
some.another.property.name -> "another value"
你的问题不是pureconfig。你的问题是 HOCON spec 你写的是什么:
cfg {
some.property.name: "value"
some.another.property.name: "another value"
}
是语法糖:
cfg {
some {
property {
name = "value"
}
}
another {
property {
name = "another value"
}
}
}
TypeSafe Config/Lightbend Config 决定您的 cfg
有两个属性,并且它们都是嵌套配置。 Pureconfig 仅采用这些嵌套配置并将它们映射到 case 类。但它无法映射结构与预期完全不同的东西。
如果你写:
cfg {
some-property-name: "value"
some-another-property-name: "another value"
}
您将能够将 "cfg"
路径解码为 Map[String, String]
并将顶级配置解码为 case class Config(cfg: Map[String, String])
。如果您想将 .
视为密钥的一部分而不是嵌套...那么恐怕您必须自己编写 ConfigReader
因为那是 non-standard 用法。
您可以通过以下方式阅读 Map[String, String]
ConfigReader
:
implicit val strMapReader: ConfigReader[Map[String, String]] = {
implicit val r: ConfigReader[String => Map[String, String]] =
ConfigReader[String]
.map(v => (prefix: String) => Map(prefix -> v))
.orElse { strMapReader.map { v =>
(prefix: String) => v.map { case (k, v2) => s"$prefix.$k" -> v2 }
}}
ConfigReader[Map[String, String => Map[String, String]]].map {
_.flatMap { case (prefix, v) => v(prefix) }
}
}
请注意,这是一个递归 val
定义,因为 strMapReader
在它自己的定义中使用。它起作用的原因是 orElse
方法按名称而不是按值获取其参数。
是否可以将 pureconfig 读取属性设置为 Map[String, String]
?我有以下
application.conf
:
cfg{
some.property.name: "value"
some.another.property.name: "another value"
}
这是我尝试读取配置的应用程序:
import pureconfig.generic.auto._
import pureconfig.ConfigSource
import pureconfig.error.ConfigReaderException
object Model extends App {
case class Config(cfg: Map[String, String])
val result = ConfigSource.default
.load[Config]
.left
.map(err => new ConfigReaderException[Config](err))
.toTry
val config = result.get
println(config)
}
问题是它抛出以下异常:
Exception in thread "main" pureconfig.error.ConfigReaderException: Cannot convert configuration to a Model$Config. Failures are:
at 'cfg.some':
- (application.conf @ file:/home/somename/prcfg/target/classes/application.conf: 2-3) Expected type STRING. Found OBJECT instead.
at Model$.$anonfun$result(Model.scala:11)
at scala.util.Either$LeftProjection.map(Either.scala:614)
at Model$.delayedEndpoint$Model(Model.scala:11)
at Model$delayedInit$body.apply(Model.scala:5)
at scala.Function0.apply$mcV$sp(Function0.scala:39)
at scala.Function0.apply$mcV$sp$(Function0.scala:39)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
at scala.App.$anonfun$main(App.scala:73)
at scala.App.$anonfun$main$adapted(App.scala:73)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
at scala.collection.AbstractIterable.foreach(Iterable.scala:920)
at scala.App.main(App.scala:73)
at scala.App.main$(App.scala:71)
at Model$.main(Model.scala:5)
at Model.main(Model.scala)
有办法解决吗?我预计 Map[String, String]
将包含以下映射:
some.property.name -> "value"
some.another.property.name -> "another value"
你的问题不是pureconfig。你的问题是 HOCON spec 你写的是什么:
cfg {
some.property.name: "value"
some.another.property.name: "another value"
}
是语法糖:
cfg {
some {
property {
name = "value"
}
}
another {
property {
name = "another value"
}
}
}
TypeSafe Config/Lightbend Config 决定您的 cfg
有两个属性,并且它们都是嵌套配置。 Pureconfig 仅采用这些嵌套配置并将它们映射到 case 类。但它无法映射结构与预期完全不同的东西。
如果你写:
cfg {
some-property-name: "value"
some-another-property-name: "another value"
}
您将能够将 "cfg"
路径解码为 Map[String, String]
并将顶级配置解码为 case class Config(cfg: Map[String, String])
。如果您想将 .
视为密钥的一部分而不是嵌套...那么恐怕您必须自己编写 ConfigReader
因为那是 non-standard 用法。
您可以通过以下方式阅读 Map[String, String]
ConfigReader
:
implicit val strMapReader: ConfigReader[Map[String, String]] = {
implicit val r: ConfigReader[String => Map[String, String]] =
ConfigReader[String]
.map(v => (prefix: String) => Map(prefix -> v))
.orElse { strMapReader.map { v =>
(prefix: String) => v.map { case (k, v2) => s"$prefix.$k" -> v2 }
}}
ConfigReader[Map[String, String => Map[String, String]]].map {
_.flatMap { case (prefix, v) => v(prefix) }
}
}
请注意,这是一个递归 val
定义,因为 strMapReader
在它自己的定义中使用。它起作用的原因是 orElse
方法按名称而不是按值获取其参数。