2016-05-07 5 views
5

Ich möchte die folgende Konfiguration aus einer HOCON (Typesafe Config) -Datei in Kotlin lesen.HOCON in Kotlin lesen und verarbeiten

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 } } 
    ] 
} 

In der Tat möchte ich die Schlüssel Spalte (n) extrahieren. Ich habe bisher folgendes versucht.

val metadata = ConfigFactory.parseFile(metafile) 
val keys = metadata.getObjectList("${tablename.toLowerCase()}.columns") 
        .filter { it.unwrapped().values.first().get("key") == true } 

Aber es schlägt mit dem folgenden Fehler fehl.

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 

Es ist klar, dass Kotlin nicht in der Lage ist, den Datentyp des „Wert“ Feld in der Map zu verstehen. Wie erkläre ich es oder lass es Kotlin wissen?

Auch nicht, dass es verschiedene Arten und optionale Schlüssel in dieser Karte gibt.

PS: Ich weiß, dass es für Kotlin einige Wrapper wie Konfig und Klutter gibt. Ich hatte gehofft, dass wenn ich leicht schreiben kann, ich eine andere Bibliothek meiden könnte.

UPDATE 1:

Ich habe folgendes versucht.

, um den folgenden Compiler-Fehler zu erhalten.

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 

Und das

it.unwrapped().values.first().get<String, Boolean?>("key") 

mit Ausgang

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 

UPDATE 2:

auf dem Weg der Suche es an anderer Stelle behandelt wird, ich denke, ich muss wahrscheinlich verwenden Betrachtung. Probieren Sie es mit meiner begrenzten Belichtung aus. Bisher kein Glück.

+0

Ich brauche wahrscheinlich nicht das Config-Objekt auszupacken. Aber so zu handeln, wie es war, brachte keine Ergebnisse, und das war das nächste, was ich dazu bringen konnte, etwas "zu drucken". –

Antwort

7

Betrachten Sie den Code, dekonstruiert unter:

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 
     } 

Aus dem oben das Problem offensichtlich sein sollte. Sie müssen die Compiler mit den Informationen helfen, dass firstValue eine hält Map etwa so:

val firstValueMap = firstValue as Map<String,Any?> 
firstValueMap["key"] == true 
+0

Danke! Es funktionierte. –

+0

Ich musste auch eine UNCHECKED_CAST Warnung unterdrücken. –

2

Auch wenn Sie sich nicht mit Klutter sind, habe ich ein Update erstellt für sie ConfigObject und Config wirkt einheitlich das gleiche machen. Ab der Version 1.17.1 von Klutter (heute Pushing zu Maven Central) können Sie basierend auf Ihrer Frage das tun, was im folgenden Komponententest dargestellt wird.

Die Funktion finden die wichtigsten Säulen:

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() 
} 

ist hier voll Unit-Test für diese:

// 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()) 
} 

Sie könnten ein Map wie oben zurückzukehren, oder eine Liste von Pair für die Spaltenname zu Einstellungszuordnung, da der Spaltenname nicht innerhalb seiner Einstellungen liegt.

Das Design der Konfigurationsdatei könnte auch geändert werden, um die Verarbeitung der Konfiguration einfacher zu machen (dh der Name der Tabelle innerhalb ihres Konfigurationsobjekts und nicht als linker Schlüssel. Gleiches für Spaltennamen, add in das Objekt und nicht als Linke-Taste.)

Verwandte Themen