uchuhimo / konf

A type-safe cascading configuration library for Kotlin/Java/Android, supporting most configuration formats
Apache License 2.0
309 stars 28 forks source link

How to write new values to config file? #1

Closed SackCastellon closed 6 years ago

SackCastellon commented 6 years ago

Is there any way to save the changed values to the same file from which the values were loaded, so when the program is run a second time then the new values are loaded?

uchuhimo commented 6 years ago

I add support for config export/import in a new release, please check it in v0.7. Now items and associated values in config can be exported to a map, which can be (de)serialized in your favorite format by your favorite serialization library, and be imported into config in the next run. I also provide an example using Java Object Serialization to (de)serialize config (see config persistence). If you need more examples or integrated support (e.g. save/load config to/from YAML/JSON/XML/...), feel free to ask me. Feedback or advice is welcome.

SackCastellon commented 6 years ago

It would be great if you could add support to save directly to JSON (for my case), or maybe provide me with an example of how to properly save to JSON because by default the format is not the right one and the workaround I found is not pretty, I would say.

Example of the problem

Spec

object server : ConfigSpec("server") {
    val port = optional("port", 3306, "The port of the server")
    val host = required<String>("host", "The host IP of the server")
    val database = required<String>("database", "The name of the database in the server")
}

object login : ConfigSpec("login") {
    val rememberUsername = optional("rememberUsername", false, "Will the last input username be remembered?")
    val username = optional("username", "", "The last input username")
}

val config = Config { addSpec(server);addSpec(login); }.withSourceFrom.json.resource("original.json")

original.json

{
  "server": {
    "host": "example.com",
    "database": "database"
  },
  "login": {
    "rememberUsername": false
  }
}

Code to save JSON

fun main(args: Array<String>) {
    FileWriter("output.json").use {
        GsonBuilder().create().toJson(config.toMap(), it)
    }
}

output.json

{
  "server.port": 3306,
  "server.host": "example.com",
  "server.database": "database",
  "login.rememberUsername": false,
  "login.username": ""
}

Workarround

original.json

{
  "server": {
    "host": "example.com",
    "database": "database"
  },
  "login": {
    "rememberUsername": false
  }
}

Code to save JSON

fun main(args: Array<String>) {
    val mapToExport = mutableMapOf<String, Any>()

    for (it in config.toTree().items) {

        if (it is OptionalItem)
            if (config[it] == it.default)
                continue // Skip if the item is optional and the value is the default

        var map = mapToExport
        it.path.dropLast(1).forEach { 
            val tmp: MutableMap<String, Any> =
                    if (map.contains(it)) map[it] as MutableMap<String, Any>
                    else mutableMapOf()
            map.put(it, tmp) // Create the path with maps of maps
            map = tmp
        }
        map.put(it.path.last(), config[it]) // Put the value of the item in the map (of maps)
    }

    FileWriter("output.json").use {
        GsonBuilder().create().toJson(mapToExport, it)
    }
}

output.json

{
  "server": {
    "host": "example.com",
    "database": "database"
  }
}
uchuhimo commented 6 years ago

@SackCastellon I guess you want to merge all updates to config with the original config file and save it as exactly the same format with the original config file. Am I right?

uchuhimo commented 6 years ago

@SackCastellon Further question: which of the following ones is your preferred use case:

  1. write back all updates to the original config file when exiting
  2. write through all updates to the original config file (keep synchronous between config and its source)
  3. save all updates to somewhere in certain format for recovering or debugging
SackCastellon commented 6 years ago

@uchuhimo

I guess you want to merge all updates to config with the original config file and save it as exactly the same format with the original config file. Am I right?

Yes

Further question: which of the following ones is your preferred use case:

  1. write back all updates to the original config file when exiting
  2. write through all updates to the original config file (keep synchronous between config and its source)
  3. save all updates to somewhere in certain format for recovering or debugging

Number 2, as soon as a value is updated write it to the config file

uchuhimo commented 6 years ago

@SackCastellon Konf is not designed for keeping synchronous between config and its source. In essence, Konf is an aggregator to collect config from different sources using schema defined by ConfigSpec, instead of a view of its sources. Thus, supporting use case 2 is not in my future plan for Konf. But I will try my best to satisfy your requirement by providing support for use case 1. I have added a new Writer API to export config to various output format, and JSON writer support is in the master branch now. In your use case, you can use the following code to write back all updates to JSON config file when exiting:

config.layer.toJson.toFile(file)

Use master snapshot of Konf to test this new feature. I will add these APIs to next release after a full test. Feedback is welcome if any bug is found.

SackCastellon commented 6 years ago

@uchuhimo There is a problem with the kotlinx-bimap dependency, the master snapshot of Konf is looking for version 0.7

image

But the latest version in your GitHub is 1.0

uchuhimo commented 6 years ago

@SackCastellon Sorry for not testing master snapshot from JitPack in advance. It seems like a bug of JitPack , and I have found a workaround for it. Please refresh your dependencies (use ./gradlew build --refresh-dependencies) and try again.

SackCastellon commented 6 years ago

@uchuhimo So far it works well. 👍 If I find any bug I'll let you know

uchuhimo commented 6 years ago

I will close this issue. You can open new issue or reopen this issue if any bug is found.

uchuhimo commented 6 years ago

The new Writer API has been fully tested. I release it in v0.8 and publish to JCenter.