微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

读取Hocon配置为Map [String,String],其中包含点符号和值

如何解决读取Hocon配置为Map [String,String],其中包含点符号和值

我有以下HOCON配置:

a {
 b.c.d = "val1"
 d.f.g = "val2" 
}

HOCON将路径“ b.c.d”和“ d.f.g”表示为对象。因此,我希望有一个读取器,将这些配置读取为Map [String,String],例如:

Map("b.c.d" -> "val1","d.f.g" -> "val2")

我创建了一个阅读器,然后尝试递归地进行操作:

import scala.collection.mutable.{Map => MutableMap}

  private implicit val mapReader: ConfigReader[Map[String,String]] = ConfigReader.fromCursor(cur => {
    def concat(prefix: String,key: String): String = if (prefix.nonEmpty) s"$prefix.$key" else key

    def toMap(): Map[String,String] = {
      val acc = MutableMap[String,String]()

      def go(
        cur: ConfigCursor,prefix: String = EMPTY,acc: MutableMap[String,String]
      ): Result[Map[String,Object]] = {
        cur.fluent.mapObject { obj =>
          obj.value.valueType() match {
            case ConfigValueType.OBJECT => go(obj,concat(prefix,obj.pathElems.head),acc)
            case ConfigValueType.STRING =>
              acc += (concat(prefix,obj.pathElems.head) -> obj.asstring.right.getorElse(EMPTY))
          }
          obj.asRight
        }
      }

      go(cur,acc = acc)
      acc.toMap
    }

    toMap().asRight
  })

它给了我正确的结果,但是这里有一种避免MutableMap的方法吗?

P.S。另外,我想保留“ pureconfig”阅读器的实现。

解决方法

您可以在不使用递归的情况下执行相同的操作。使用方法entrySet如下

import scala.jdk.CollectionConverters._

val hocon =
  """
  |a {
  | b.c.d = "val1"
  | d.f.g = val2 
  |}""".stripMargin
val config  = ConfigFactory.load(ConfigFactory.parseString(hocon))
val innerConfig = config.getConfig("a")

val map = innerConfig
  .entrySet()
  .asScala
  .map { entry =>
    entry.getKey -> entry.getValue.render()
  }
  .toMap

println(map)

生产

Map(b.c.d -> "val1",d.f.g -> "val2")

只要具备一定的知识,就可以定义一个pureconfig.ConfigReader,其内容如下:{p}

Map[String,String]
,

Ivan Stanislavciuc提供的解决方案并不理想。如果已解析的配置对象包含字符串或对象以外的值,则不会收到错误消息(如您​​期望的那样),而是显示一些非常奇怪的输出。例如,如果您解析这样的类型安全配置文件

File inputFile = new File("file name");
ReversedLinesFileReader reader = new ReversedLinesFileReader(inputFile,StandardCharsets.UTF_8);
Stack<String> lines = new Stack<>();
String lineContent;
int count = 0;
while ((lineContent= reader.readLine()) != null && count++ < 5000) {
    lines.add(lineContent);
}

while (!lines.isEmpty()) {
    System.out.println(lines.pop());
}

结果值将如下所示:

"a":[1]

即使输入仅包含对象和字符串,它也无法正常工作,因为它错误地在所有字符串值周围添加了双引号。

所以我自己给了一个机会,并提出了一个递归解决方案,该解决方案报告了诸如列表或null之类的错误,并且不添加不应存在的引号。

Map(a -> [
    # String: 1
    1
])

请注意,我的解决方案根本没有提到 implicit val reader: ConfigReader[Map[String,String]] = { implicit val r: ConfigReader[String => Map[String,String]] = ConfigReader[String] .map(v => (prefix: String) => Map(prefix -> v)) .orElse { reader.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) } } } ConfigValue。它仅采用现有的ConfigReader.Result对象,并将它们与诸如ConfigReadermap之类的组合器组合在一起。一般来说,这是编写orElse的最佳方法:不要从头开始使用ConfigReader之类的方法,不要使用现有的阅读器并将其组合。

起初,上面的代码完全起作用似乎有点令人惊讶,因为我在自己的定义中使用了ConfigReader.fromFunction。但这是有效的,因为reader方法按名称而不是按值接受参数。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。