如何解决读取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
对象,并将它们与诸如ConfigReader
和map
之类的组合器组合在一起。一般来说,这是编写orElse
的最佳方法:不要从头开始使用ConfigReader
之类的方法,不要使用现有的阅读器并将其组合。
起初,上面的代码完全起作用似乎有点令人惊讶,因为我在自己的定义中使用了ConfigReader.fromFunction
。但这是有效的,因为reader
方法按名称而不是按值接受参数。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。