【问题标题】:Pureconfig read config as properties mapPureconfig 将配置读取为属性映射
【发布时间】:2021-01-31 08:49:48
【问题描述】:

是否可以让 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$2(Model.scala:11)
    at scala.util.Either$LeftProjection.map(Either.scala:614)
    at Model$.delayedEndpoint$Model$1(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$1(App.scala:73)
    at scala.App.$anonfun$main$1$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"

【问题讨论】:

    标签: scala pureconfig


    【解决方案1】:

    您的问题不是 pureconfig。你的问题是你写的 HOCON 规范:

    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 只接受这些嵌套配置并将它们映射到案例类。但它无法映射结构与预期完全不同的东西。

    如果你写:

    cfg {
      some-property-name: "value"
      some-another-property-name: "another value"
    }
    

    您将能够将"cfg" 路径解码为Map[String, String],并将顶级配置解码为case class Config(cfg: Map[String, String])。如果您想将. 视为键的一部分而不是嵌套...那么恐怕您必须自己编写ConfigReader,因为这是非标准用法。

    【讨论】:

    • 问题的背景是允许用户以properties-file格式添加config参数。
    【解决方案2】:

    您可以使用以下ConfigReader 以这种方式阅读Map[String, String]

    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 方法通过名称而不是值获取其参数。

    【讨论】:

      猜你喜欢
      • 2021-07-01
      • 2021-03-14
      • 2021-01-24
      • 1970-01-01
      • 2022-06-21
      • 2013-04-23
      • 1970-01-01
      • 1970-01-01
      • 2021-04-17
      相关资源
      最近更新 更多