在Kotlin阅读和处理HOCON

我想从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 } } ] } 

事实上,我想提取关键列(S)。 到目前为止,我尝试了以下方法。

 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知道?

也不是说在这个地图中有不同的类型和可选的键。

PS:我知道Kontrig和Klutter有Kotlin可用的包装。 我希望如果这很容易写,我可以避免另一个图书馆。

更新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,我也创建了一个更新来使ConfigObjectConfig一致。 从Klutter版本1.17.1开始(今天推送到Maven中心),您可以根据您的问题做以下单元测试所代表的内容。

找到关键列的函数:

 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 http://stackoverflow.com/questions/37092808/reading-and-processing-hocon-in-kotlin @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()) } 

因为列名不在其设置范围内,所以可以像上面那样返回一个Map ,或者将列名Pair的列表返回到设置映射。

配置文件的设计也可以改变,使配置的处理更加简单(即配置对象中的表的名称,而不是左侧的键),相同的列名,添加到对象和而不是作为左侧键)。

    Interesting Posts