在 Kotlin 中读取和处理 HOCON
Reading and Processing HOCON in Kotlin
我想从 HOCON(Typesafe Config)文件中将以下配置读入 Kotlin。
tablename: {
columns: [
{ item: { type: integer, key: true, null: false } }
{ desc: { type: varchar, length: 64 } }
{ quantity: { type: integer, null: false } }
{ price: { type: decimal, precision: 14, scale: 3 } }
]
}
事实上,我想提取关键列。到目前为止,我已经尝试了以下方法。
val metadata = ConfigFactory.parseFile(metafile)
val keys = metadata.getObjectList("${tablename.toLowerCase()}.columns")
.filter { it.unwrapped().values.first().get("key") == true }
但失败并出现以下错误。
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
@kotlin.internal.InlineOnly public operator inline fun <@kotlin.internal.OnlyInputTypes K, V> kotlin.collections.Map<out kotlin.String, ???>.get(key: kotlin.String): ??? defined in kotlin.collections
很明显,Kotlin 无法理解 Map 中 "value" 字段的数据类型。我如何声明它或让 Kotlin 知道?
也不是这个Map中有不同的类型和可选键。
PS:我知道有几个包装器可用于 Kotlin,例如 Konfig 和 Klutter。我希望如果这很容易写,我可以避免使用另一个库。
更新 1:
我试过以下方法。
it.unwrapped().values.first().get<String, Boolean>("key")
得到以下编译器错误。
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
@kotlin.internal.InlineOnly public operator inline fun <@kotlin.internal.OnlyInputTypes K, V> kotlin.collections.Map<out kotlin.String, kotlin.Boolean>.get(key: kotlin.String): kotlin.Boolean? defined in kotlin.collections
还有这个
it.unwrapped().values.first().get<String, Boolean?>("key")
有输出
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
@kotlin.internal.InlineOnly public operator inline fun <@kotlin.internal.OnlyInputTypes K, V> kotlin.collections.Map<out kotlin.String, kotlin.Boolean?>.get(key: kotlin.String): kotlin.Boolean? defined in kotlin.collections
更新 2:
看其他地方的处理方式,我想我可能需要使用反射。在我有限的曝光下尝试一下。到目前为止运气不好。
考虑你的代码,解构如下:
val keys = metadata.getObjectList("tablename.columns")
.filter {
val item:ConfigObject = it
val unwrapped:Map<String,Any?> = item.unwrapped()
val values:Collection<Any?> = unwrapped.values
val firstValue:Any? = values.first()
firstValue.get("key") == true // does not compile
}
从上面的问题应该很明显了。您需要用 firstValue
包含 Map
的信息来帮助编译器,如下所示:
val firstValueMap = firstValue as Map<String,Any?>
firstValueMap["key"] == true
即使您没有使用 Klutter,我也为其创建了一个更新,使 ConfigObject
和 Config
的行为一致。从 Klutter 版本 1.17.1
开始(今天推送到 Maven Central),您可以根据您的问题执行以下单元测试中表示的内容。
查找关键列的函数:
fun findKeyColumns(cfg: Config, tableName: String): Map<String, ConfigObject> {
return cfg.nested(tableName).value("columns").asObjectList()
.map { it.keys.single() to it.value(it.keys.single()).asObject() }
.filter {
it.second.value("key").asBoolean(false)
}
.toMap()
}
这是完整的单元测试:
// from
@Test fun testFromSo37092808() {
// === mocked configuration file
val cfg = loadConfig(StringAsConfig("""
products: {
columns: [
{ item: { type: integer, key: true, null: false } }
{ desc: { type: varchar, length: 64 } }
{ quantity: { type: integer, null: false } }
{ price: { type: decimal, precision: 14, scale: 3 } }
]
}
"""))
// === function to find which columns are key columns
fun findKeyColumns(cfg: Config, tableName: String): Map<String, ConfigObject> {
return cfg.nested(tableName).value("columns").asObjectList()
.map { it.keys.single() to it.value(it.keys.single()).asObject() }
.filter {
it.second.value("key").asBoolean(false)
}
.toMap()
}
// === sample usage
val productKeys = findKeyColumns(cfg, "products")
// we only have 1 in the test data, so grab the name and the values
val onlyColumnName = productKeys.entries.first().key
val onlyColumnObj = productKeys.entries.first().value
assertEquals ("item", onlyColumnName)
assertEquals (true, onlyColumnObj.value("key").asBoolean())
assertEquals ("integer", onlyColumnObj.value("type").asString())
assertEquals (false, onlyColumnObj.value("null").asBoolean())
}
您可以 return 一个 Map
如上所述,或者 Pair
的列表用于列名到设置的映射,因为列名不在其设置内。
配置文件的设计也可以改变,使配置的处理更简单(即 table 在其配置对象中的名称,而不是作为左侧键。相同对于列名,添加到对象中而不是作为左侧键。)
我想从 HOCON(Typesafe Config)文件中将以下配置读入 Kotlin。
tablename: {
columns: [
{ item: { type: integer, key: true, null: false } }
{ desc: { type: varchar, length: 64 } }
{ quantity: { type: integer, null: false } }
{ price: { type: decimal, precision: 14, scale: 3 } }
]
}
事实上,我想提取关键列。到目前为止,我已经尝试了以下方法。
val metadata = ConfigFactory.parseFile(metafile)
val keys = metadata.getObjectList("${tablename.toLowerCase()}.columns")
.filter { it.unwrapped().values.first().get("key") == true }
但失败并出现以下错误。
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
@kotlin.internal.InlineOnly public operator inline fun <@kotlin.internal.OnlyInputTypes K, V> kotlin.collections.Map<out kotlin.String, ???>.get(key: kotlin.String): ??? defined in kotlin.collections
很明显,Kotlin 无法理解 Map 中 "value" 字段的数据类型。我如何声明它或让 Kotlin 知道?
也不是这个Map中有不同的类型和可选键。
PS:我知道有几个包装器可用于 Kotlin,例如 Konfig 和 Klutter。我希望如果这很容易写,我可以避免使用另一个库。
更新 1:
我试过以下方法。
it.unwrapped().values.first().get<String, Boolean>("key")
得到以下编译器错误。
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
@kotlin.internal.InlineOnly public operator inline fun <@kotlin.internal.OnlyInputTypes K, V> kotlin.collections.Map<out kotlin.String, kotlin.Boolean>.get(key: kotlin.String): kotlin.Boolean? defined in kotlin.collections
还有这个
it.unwrapped().values.first().get<String, Boolean?>("key")
有输出
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
@kotlin.internal.InlineOnly public operator inline fun <@kotlin.internal.OnlyInputTypes K, V> kotlin.collections.Map<out kotlin.String, kotlin.Boolean?>.get(key: kotlin.String): kotlin.Boolean? defined in kotlin.collections
更新 2:
看其他地方的处理方式,我想我可能需要使用反射。在我有限的曝光下尝试一下。到目前为止运气不好。
考虑你的代码,解构如下:
val keys = metadata.getObjectList("tablename.columns")
.filter {
val item:ConfigObject = it
val unwrapped:Map<String,Any?> = item.unwrapped()
val values:Collection<Any?> = unwrapped.values
val firstValue:Any? = values.first()
firstValue.get("key") == true // does not compile
}
从上面的问题应该很明显了。您需要用 firstValue
包含 Map
的信息来帮助编译器,如下所示:
val firstValueMap = firstValue as Map<String,Any?>
firstValueMap["key"] == true
即使您没有使用 Klutter,我也为其创建了一个更新,使 ConfigObject
和 Config
的行为一致。从 Klutter 版本 1.17.1
开始(今天推送到 Maven Central),您可以根据您的问题执行以下单元测试中表示的内容。
查找关键列的函数:
fun findKeyColumns(cfg: Config, tableName: String): Map<String, ConfigObject> {
return cfg.nested(tableName).value("columns").asObjectList()
.map { it.keys.single() to it.value(it.keys.single()).asObject() }
.filter {
it.second.value("key").asBoolean(false)
}
.toMap()
}
这是完整的单元测试:
// from
@Test fun testFromSo37092808() {
// === mocked configuration file
val cfg = loadConfig(StringAsConfig("""
products: {
columns: [
{ item: { type: integer, key: true, null: false } }
{ desc: { type: varchar, length: 64 } }
{ quantity: { type: integer, null: false } }
{ price: { type: decimal, precision: 14, scale: 3 } }
]
}
"""))
// === function to find which columns are key columns
fun findKeyColumns(cfg: Config, tableName: String): Map<String, ConfigObject> {
return cfg.nested(tableName).value("columns").asObjectList()
.map { it.keys.single() to it.value(it.keys.single()).asObject() }
.filter {
it.second.value("key").asBoolean(false)
}
.toMap()
}
// === sample usage
val productKeys = findKeyColumns(cfg, "products")
// we only have 1 in the test data, so grab the name and the values
val onlyColumnName = productKeys.entries.first().key
val onlyColumnObj = productKeys.entries.first().value
assertEquals ("item", onlyColumnName)
assertEquals (true, onlyColumnObj.value("key").asBoolean())
assertEquals ("integer", onlyColumnObj.value("type").asString())
assertEquals (false, onlyColumnObj.value("null").asBoolean())
}
您可以 return 一个 Map
如上所述,或者 Pair
的列表用于列名到设置的映射,因为列名不在其设置内。
配置文件的设计也可以改变,使配置的处理更简单(即 table 在其配置对象中的名称,而不是作为左侧键。相同对于列名,添加到对象中而不是作为左侧键。)