【问题标题】:Reading and Processing HOCON in Kotlin在 Kotlin 中读取和处理 HOCON
【发布时间】:2016-05-07 19:28:18
【问题描述】:

我想从 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 中“值”字段的数据类型。如何声明或让 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:

看看其他地方的处理方式,我想我可能需要使用反射。在我有限的曝光范围内尝试一下。到目前为止没有运气。

【问题讨论】:

  • 我可能不需要解开配置对象。但是按原样处理它并没有产生任何结果,这是我可以带它来“打印”某些东西的最接近的方法。

标签: kotlin typesafe-config hocon


【解决方案1】:

考虑您的代码,解构如下:

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

【讨论】:

  • 我也不得不取消 UNCHECKED_CAST 警告。
【解决方案2】:

即使您没有使用 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 列表作为列名到设置的映射,因为列名不在其设置中。

配置文件的设计也可以改变,使配置的处理更简单(即配置对象内的表名,而不是左侧键。列名也一样,添加到对象而不是左侧键。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 2013-06-16
    • 2021-02-25
    • 2019-02-17
    • 2012-09-10
    • 1970-01-01
    相关资源
    最近更新 更多