uchuhimo / konf Goto Github PK
View Code? Open in Web Editor NEWA type-safe cascading configuration library for Kotlin/Java/Android, supporting most configuration formats
License: Apache License 2.0
A type-safe cascading configuration library for Kotlin/Java/Android, supporting most configuration formats
License: Apache License 2.0
It would be nice if konf offered the feature to merge map keys instead of replacing whole map from a high precedence configuration file.
Just like spring boot properties work: https://stackoverflow.com/questions/45859131/spring-boot-merging-map-of-map-property
Another example of this:
Suppose I have this two configuration files, with the second file overriding the first one:
config1.yaml
configuration:
property_map:
key1: value1
key2: value2
config2.yaml
configuration:
property_map:
key1: value3
And my code looks like this:
fun main() {
var config = Config { addSpec(ConfigurationSpec) }.disable(FAIL_ON_UNKNOWN_PATH)
.from.yaml.resource("config1.yaml")
.from.yaml.resource("config2.yaml")
val propertyMap = config.getOrNull<Map<String, String>>("configuration.property_map")
println(propertyMap?.get("key1"))
println(propertyMap?.get("key2"))
}
object ConfigurationSpec : ConfigSpec() {
val propertyMap by optional(emptyMap<String, String>(), name = "property_map")
}
I would like to have the following output from running the above code:
value3
value2
But instead I get:
value3
null
Because it replaces the map instead of merging them.
I apologise if this is intended and documented somewhere, I couldn't find it mentioned anywhere.
Consider this yaml config file:
tree:
1:
myVal: true
This will fail to resolve properly (i.e. config.at("tree.1.myVal").toValue<Boolean>())
).
However, if that 1
is renamed to a1
, everything works properly.
I would like to have optional nullable config properties.
This is for the case where the property is a class object not a primative so simple defaults dont work well.
Example
object BootstrapConfig : ConfigSpec("bootstrap.config")
{
data class S3Config(val url: String, val region: String ="us-east-1")
var s3 = required<S3Config>("s3")
val url = optional<String>("url","")
val file = optional<String>("file","")
}
I would like s3 to be optional as the above config is intended as a 'union type' -- only 1 of the 3 are needed. Ideally all 3 should be nullable optional.
I can create a no-arg constructor for S3Config but that is non-ideal as in other cases it may be a fairly large object or testing for 'empty' or 'exists' is non-trivial vs testing for a supplied value with empty fields.
It appears from the code that it may be nearly as simple as changing the template types to accept <Any?> instead of
That would allow either optional<S3Config?>("s3",null) or required("s3")
I did see the Item.getOrNull() but not sure how to used that correctly .. I think it was intended for the ase of a named item not existing, not for the value being empty.
Perhaps the UNSET State can be used in some way.
if( config[Config.s3].isUnset() ) null else config[Config.s3] // verbose even if it works
Perhaps another alternative is to make the whole thing 1 class
data class S3Config(val url: String, val region: String ="us-east-1")
data class MyConfg( val s3: S3Config? , val url: String? , val file: String?)
object BootstrapConfig : ConfigSpec("bootstrap")
{
var config = required<MyConfig>("config")
}
I havent tried that yet, not sure if the value prefix's will work in this nested of a config.
e.g. will "bootstrap.config.s3.url" resolve properly to env var "BOOTSTRAP_CONFIG_S3_URL"
or to property "bootstrap.config.s3.url"
Suggestions welcome
A highly use feature in config systems is runtime value interpolation. HCON implements this fairly crudely. A good example is log4j's configuration
This is equivalent to kotlin or groovy interpolated strings except the string to be interpolated comes from within the config values itself not in the kotlin code.
As in HCON, it is useful to be able to evaluate the values lazily so they can contain references to other values not necessarily from the same file. When using multiple formats its tricky to get right, but I belive using the property format would work well in all source formats like:
in source1.json
{
"base" : { "user" : "a user" , "password" : "a password" }
}
in source2.properties
connection.jdbc=jdbc:mysql:http://${base.user}:${base.password}@server:port
Then in the app when getting 'connection.jdbc' it gets "jdbc:mysql:http://user:a password@server:port"
This can currently be done with some complex specs and lazy values or properties on a ad-hoc basis, and possibly with some kind of overloaded get() functions but its not obvious exactly how without writing a full new layer of access.
From what I can see in the code I think it would be fairly simple to implement as a direct feature, maybe with some overloading to be supplied by the user for extension.
E.g. the log4j syntax uses ${source:value} , where "source" is composed of a set of builtin and user defined configuration sources similar to konf Source's e.g. ${env:ENV_VAR} where "env" is mapped to the the environment variable source.
There might be a way of leveraging the spec prefix, config layer , loader or source names or prefixes to default much of this behaviour.
Suggestions welcome for where you think a feature of this sort 'naturally' belongs.
--
A related concept is to allow object level merging specified in the config files themselves.
HCON does this but in a very crude way (via string interpolation not structural merging).
e.g. imaging the above case but instead of string interpolation, structural merging is done
source2.properties
connection.jdbc = ${base}
connection.jdbc.server = mysql.com
connection.jsbc.user = differentuser
connection.jdbc.url = http://${user}:${password}@${server}
It may be useful to use a different syntax for structural merging then value substitution, e.g. maybe something like ${base.*} or ${@base} ... something to indicate to pull the subtree rooted at '${base}' into the current context.
It seems like this could be accomplished by creating a new layer implicitly when a structural reference is found injecting it into the current prefix. From there the normal default processing would work as if the subtree were actually inline.
I need different contexts of the same config file schema in my project. So let´s say one has a api.foo.yaml and a api.bar.yaml in which we have the same keys but with different values.
// api.foo.yaml
database:
host: foo.com
// api.bar.yaml
database:
host: bar.com
And I create a Config class from each file it only has the foo values, since it was loaded first.
This is because the Spec classes are singeltons (object) in Kotlin.
I don´t know if I would consider it a issue. Maybe more a feature.
Does your Konf
library support HOCON substitutions (${foo.bar}
)? It throws me ConfigException
: com.typesafe.config.ConfigException$NotResolved: need to Config#resolve()
.
It's impossible to use Konf out of the box, since it needs additional dependency kotlinx-bimap
and shows error.
So we forced to include both artifacts to POM:
<dependency>
<groupId>com.github.uchuhimo</groupId>
<artifactId>kotlinx-bimap</artifactId>
<version>v1.1</version>
</dependency>
<dependency>
<groupId>com.github.uchuhimo</groupId>
<artifactId>konf</artifactId>
<version>v0.11</version>
</dependency>
It make sense to update repo with new version with full dependencies included or specify this requirement in readme
.
PS: Thanks for your great work!
Newbie to Kotlin and Konf here. Is there an easier way to achieve mapping the following yaml to the Spec below:
Or does the solution involve using the jackson mapper with config.mapper? Thank you !
datasets:
hive:
- key: transactions
uri: /user/somepath
format: parquet
database: transations_daily
table: transx
- key: second_transactions
uri: /seconduser/somepath
format: avro
database: transations_monthly
table: avro_table
file:
- key: users
uri: s3://filestore
format: parquet
mode: overwrite
object DataSetsSpec: ConfigSpec("datasets"){
val fileDataSets by optional<List<FileDS>>(default = emptyList())
val hive by optional<List<HiveDS>>(default = emptyList())
}
object FileDS: ConfigSpec ("file"){
val key by required<String>(description = "Name of the dataset")
val uri by required<String>(description = "Target path of the file with the protocol qualifier")
val format by optional<String>("parquet", "File format", "Format in which the file must be writte")
val mode by optional<String>("append", "Save Mode", "Mode in which the file would be written - (overwrite, append)")
}
object HiveDS: ConfigSpec ("hive"){
val key by required<String>()
val uri by required<String>()
val database by required<String>()
val table by required<String>()
val format by optional<String>("parquet", "File format", "Format in which the file must be writte")
val mode by optional<String>("append", "Save Mode", "Mode in which the file would be written - (overwrite, append)")
}
I encountered an issue with reading configuration from the environment which made me realize keys are apparently case-sensitive. This is particularly annoying when keys from the environment are all lower-case'd while my ConfigSpec uses camelCase (e.g: DATASOURCECLASSNAME does not map to dataSourceClassName)
Have you considered the ability to resolve keys case-insensitively?
Say I have 3 entries in my configuration A: Int
, B: String
and C: Float
. Now I got a map like [A = 10, B = "ciao"]
and i want to set those values. One way would be to write this file as a JSON and reload from file, is it the only way? It would be perfect something like conf.loadFromMap(myMap)
. Or maybe myMap.forEach { k, v -> conf[k] = v }
I have environment variables that I'd like to access via config specs:
export KAFKA_CLIENT_CERT=some_cert
export KAFKA_CLIENT_CERT_KEY=some_cert_key
Here's my config spec:
object KafkaConfigSpec : ConfigSpec("kafka") {
val clientCert by optional(name="client.cert", default = "")
val clientCertKey by optional(name="client.cert.key", default = "")
}
When I try to load the config:
Config { addSpec(KafkaConfigSpec) }.withSourceFrom.env()
I get the following stack trace:
Exception in thread "main" com.uchuhimo.konf.NameConflictException: item kafka.client.cert.key cannot be added
at com.uchuhimo.konf.BaseConfig.addSpec(BaseConfig.kt:415)
at com.uchuhimo.konf.Config$Companion.invoke(Config.kt:344)
Great work, I was thinking of using it in my current project. One requirement I have, though, is to allow strict parsing, meaning that if a key appears in the configuration, and this key is not part of the Spec, the application should fail to start.
Ideally, there should be a function, at Config level, returning all the keys that are not part of the schema.
I am trying to integrate Konf lib with android app but facing issues while creating Config object.
java.lang.ExceptionInInitializerError I am getting this exception while creating Config object.
When using something like .withSourceFrom.env()
or .withSourceFrom.systemProperties()
for an OptionalItem<List<String>>
for example, the current implementation expects multiple environment variables defined like this: MY_KEY_0
, MY_KEY_1
and so on. This is quite inconvenient.
It would be nice to parse MY_KEY
either as a JSON or comma separated values.
A some of the "spek" tests are suffixed with Spec
instead of Spek
.
Hey @uchuhimo thanks again for your great work here. It would be great to be able to specify required
and optional
nested object properties.
At the moment, this is technically possible, but a bit awkward, requiring to use a Map<String, Any>
, and then using that to create a Config
, perhaps with one or more Spec
s.
Ideally, the following should work:
val outer = object : ConfigSpec() {
val inner = object : ConfigSpec("address") {
val host by required<String>()
val port by required<Int>()
}
val settings by required<Config>().withSchema(inner)
}
val configuration = Config.invoke { addSpec(outer) }.from.map.kv(mapOf("settings.address.host" to "127.0.0.1", "settings.address.port" to 8080))
val settings = configuration[outer.settings]
val host = settings[outer.inner.host]
Not sure if there's a better way of doing the above, perhaps by using multiple Spec
s.
config.validateRequired()
does not check for empty values only nullsname
, does not telling which exact item is not set.For instance if you have a configuration like this:
host:
url:
port:
database:
url:
port:
UnsetValueException will for instance throw "item url not set", better would be "host.url not set"
Is it possible to create a config property which is optional without having to provide a default value? Thinking something like the below which can be retrieved from the Config object as a Kotlin nullable variable. Have the case where a config value is either defined or not, there would be no default value.
object AppSpec : ConfigSpec() {
val host by required("0.0.0.0")
val resourcePath by optional<String>() // no default value
}
Then when retrieved something like
// as nullable var
val resourcePath : String? = config[AppSpec.resourcePath]
(Great library by the way)
Hello!
Thanks a lot for all the good work you put in that library, it looks really nice!
I tested it on simple use cases and everything is going really fine for me, but I have one use case I'd like to use it for, and I can't manage to find a solution yet.
I had a look at the documentation, the source code, and all past issues, but couldn't manage to find an answer, which is why I created this issue.
My concern is pretty simple, right now I'm writting this:
val configuration = Config { addSpec(ServerSpec) }
.from.yaml.resource("production.yml")
.from.yaml.resource("local.yml")
.from.yaml.watchFile("./debug.yml")
.from.env()
.from.systemProperties()
Because what I'd like to have is:
production.yml
resource (which will always be there, and packaged for production)local.yml
file as a resource as well, but it is ignored by Git, so it won't be packaged. I'd like to load it when it is present, but it'll obviously won't be there on production,debug.yml
is present at the path I specified, I'd like to load it and use its values, otherwise, I'd like it to be ignored.Right now my issue is that if something isn't present, I have exceptions such as: java.io.FileNotFoundException: config.yml (No such file or directory)
. Which is normal since the file doesn't exist, but I'd like the framework just to ignore that source silently.
From my point of view, I could either write something like:
val configuration = Config { addSpec(ServerSpec) }
.disable(Feature.FAIL_ON_UNFOUND_SOURCE)
.from.yaml.resource("production.yml")
.from.yaml.resource("local.yml")
.from.yaml.watchFile("./debug.yml")
.from.env()
.from.systemProperties()
or something like:
val configuration = Config { addSpec(ServerSpec) }
.from.optional.yaml.resource("production.yml")
.from.optional.yaml.resource("local.yml")
.from.optional.yaml.watchFile("./debug.yml")
.from.env()
.from.systemProperties()
Of course if there are any other solutions (existing or not) you have in mind, do not hesitate to let me know.
What do you think about this use case? If I can be of any help regarding the resolution of that issue, do not hesitate to tell me.
Thanks a lot for your attention and your help! Have a great day!
Hi - first time using this (it's really nice BTW), and I think I found a minor issue.
using the auto-extension discovery feature, it looks like it's limited to json
and properties
file types currently. e.g. If I send a .toml
file to this function:
fun loadConfig(configFile: File): AppConfig {
return Config()
.from.file(configFile)
.toValue()
}
it fails with com.uchuhimo.konf.source.UnsupportedExtensionException: cannot detect supported extension for "test-config.toml", supported extensions: conf, json, properties, toml, xml, yml, yaml
.
It looks like this is because of DefaultLoaders:137, where it calls
fun of(extension: String): Provider? =
extensionToProvider[extension]
whose implementation is this:
private val extensionToProvider = ConcurrentHashMap(mutableMapOf(
"json" to JsonProvider,
"properties" to PropertiesProvider
))
my sample loadConfig works fine if I use .from.toml.file(configFile)
.
See anything I'm doing wrong? It's not a big deal either way. Thanks.
Hi,
where can I find a library of examples for konf? I'm trying to get past the base example presented, but I can't understand how to do more advanced stuff with the library.
I want to parse e.g.:
tests:
- description: "latex guide should build"
configuration:
tasks: [ "buildLatex" ]
options: [ "--debug" ]
expectation:
file_exists: [ "output.pdf" ]
I tried the following:
data class Configuration(val tasks: List<String>, val options: List<String>)
data class Expectation(val file_exists: List<String>)
data class Test(val description: String, val configuration: Configuration, val expectation: Expectation)
object Root : ConfigSpec("") {
val tests by required<List<Test>>()
}
val testConfiguration = Config().from.yaml.inputStream(myInputStream)
println(testConfiguration[Root.tests])
But this causes:
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at kotlin.reflect.jvm.internal.calls.CallerImpl$Constructor.call(CallerImpl.kt:41)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:106)
at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:152)
at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:110)
at kotlin.reflect.full.KClasses.createInstance(KClasses.kt:283)
at io.kotlintest.runner.jvm.JvmKt.instantiateSpec(jvm.kt:15)
at io.kotlintest.runner.jvm.TestEngine.createSpec(TestEngine.kt:122)
at io.kotlintest.runner.jvm.TestEngine.access$createSpec(TestEngine.kt:19)
at io.kotlintest.runner.jvm.TestEngine$submitSpec$1.run(TestEngine.kt:105)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: com.uchuhimo.konf.NoSuchItemException: cannot find item tests in config
at com.uchuhimo.konf.BaseConfig.getOrNull(BaseConfig.kt:248)
at com.uchuhimo.konf.BaseConfig.getOrNull(BaseConfig.kt:245)
at com.uchuhimo.konf.BaseConfig.getOrNull$default(BaseConfig.kt:188)
at com.uchuhimo.konf.BaseConfig.get(BaseConfig.kt:168)
at my.call.site
It's unclear to me why and how can I debug this.
Thanks!
I am trying to accomplish the following:
In one source (or ideally in the same source, but for simplictiy, 1 source )
base.properties:
auth.user=username
auth.password=password
In another source I would like to reuse 'auth' as a default for other values.
service.properties
service1.url=http://myservice
service2.url=http://myservice
service2.auth.user=user1
service2.auth.password=user2
In Application
data class Auth(
val user="",
val password=""
)
object ServiceSpec : ConfigSpec() {
val url by required<String>()
val auth by required<Auth>()
}
I would like to instantiate something like
class Service( config: Config ) {
val url by config.property( ServiceSpec.url )
val auth by config.property( ServiceSpec.auth )
}
Then in 'pseudo code'
val authConfig = Config { addSpec( ? ) }.withSourceFrom.file("base.properties")
val ServiceConfig = Config { addSpec( ServiceSpec( ? ) } ??
val services = Config( ServiceConfig{ ? } ).withSourceFrom("service.properties")
val service1 = Service( ServiceConfig("service1" ).withValuesFrom(services) ?? .withDefaultsFrom( authConfig ) )
val service2 = Service( ServiceConfig("service2") .withValuesFrom(services) ?? .withDefaultsFrom(authConfig))
Couple unresolved issues
For while I thought the FlatSource( prefix="xxx" ) might work, but it does not. The prefix's are all absolute. Unless I am missing something you cannot derive specs from other specs or load sources or values into any but the root prefix.
This is a very similar but orthagonal feature that layers provide. Layers let you have defaults on the same prefix but different named layers.
Is there currently a way, or is it conceviable to implement something like
Config.loadSourceAt("prefix.nested").from.file("file")
and/or
ConfigSpec( parent: ConfigSpec , atPrefix: String )
Essentially be able to define ConfigSpecs as reusable types that may occur at different places in a config hierarchy, AND be able to load Sources from the same or different locations into different prefix'ed roots.
I am using this library to create temporary configuration files in folders that may be removed by the program or another process later. If I update config and export it to file with .toYaml.toFile(file)
, the file becomes locked, cause the library doesn't close the OutputStream after the write.
Try running this test (I'm using Apache Commons to delete the file)
internal class Test {
private object TestSpec : ConfigSpec() {
val testVal by optional("test")
}
@Test
fun testKonfFileAccess() {
val file = File.createTempFile("test", ".yaml")
Config { addSpec(TestSpec) }
.toYaml.toFile(file)
FileUtils.forceDelete(file)
}
}
You would get an IOException.
As a workaround you can write the file contents with kotlin's writeText:
file.writeText(
Config { addSpec(TestSpec) }
.toYaml.toText()
)
But I belive that's this is not an intended behavior.
Hey, I've been trying to map toml table array but it returns an empty list.
TOML:
[[editors]]
id = "bytespy.jvm.bytecode.editor"
impl = "dev.cubxity.bytespy.jvm.impl.editor.BytecodeContentEditor"
content = "org.objectweb.asm.tree.ClassNode"
description = "JVM Bytecode editor"
[[editors]]
id = "bytespy.jvm.bytecode.decompiler"
impl = "dev.cubxity.bytespy.jvm.impl.editor.DecompiledContentEditor"
content = "org.objectweb.asm.tree.ClassNode"
description = "JVM Bytecode decompiler"
ConfigSpec:
object ManifestSpec : ConfigSpec() {
val editors by optional(listOf<Editor>())
}
Editor:
data class Editor (
val id: String,
val impl: String,
val content: String,
val description: String
)
Any help would be appreciated :)
If yes, even from resources?
Can you elaborate an example?
val configDefault = Config {addSpec(ApplicationSpec) }
.from.yaml.resource("default.yml")
val configEnv = Config{ addSpec(ApplicationSpec) }
.from.env() // SERVER_HOST=0.0.0.0
.from.systemProperties() // -Dserver.host=0.0.0.0 -Dserver.port=8080
val totalConfig = configDefault + configLoad
totalConfig.contains("value) // this will show true
totaConfig<String>("value) // this will fail
I currently think the amount of supported formats and dependencies is excessive for most projects. I think we should move the JSON, XML, YAML, HOCON, and TOML providers into separate modules, and let users opt-in to use these different providers. Furthermore I also think it would be a good idea to move the functionality for loading from git repositories into a different module. The primary motivation for this is to reduced amount of dependencies on the core library.
What I propose is to have 7 different modules in total;
konf-core
konf-json
konf-xml
konf-yaml
konf-hocon
konf-toml
konf-jgit
I've forked the project and started making progress on this in a separate branch. I intend the API to stay mostly unchanged, however it's necessary to remove some fields and replace them with extension properties (DefaultLoaders
specifically). The README and documentation must be updated to reflect these few changes, and add that you must manually add providers as a dependency, and register them to the Provider
companion object.
I expect this to throw an exception when executed because I don't have either value defined in my Environment:
object DatabaseSpec : ConfigSpec(){
val connectionString by required<String>()
val driver by required<String>()
}
val config = Config{
addSpec(DatabaseSpec)
}
.from.env()
But it does not throw an exception. Am I misunderstanding what "required" means?
1. To reproduce the error run the following code.:
Main.kt
val config = Config { addSpec(ConfigurationSpec) }.enable(Feature.OPTIONAL_SOURCE_BY_DEFAULT)
.from.json.file("config.json")
.from.properties.file("config.prop")
.from.properties.file("config.properties")
.from.hocon.file("config.conf")
.validateRequired()
.toValue<Configuration>()
ConfigurationSpec.kt:
package io.github.xyz.xyz
import com.uchuhimo.konf.*
import io.github.animeshz.air_bent.*
import java.io.*
import java.nio.file.*
object ConfigurationSpec : ConfigSpec("configuration") {
object Server : ConfigSpec("server") {
val port by optional(80)
val dbFile by optional("${Paths.get("").toAbsolutePath().toString().trimEnd(File.separatorChar) + File.separator}data.db", "database file")
}
object Hardware : ConfigSpec("hardware") {
val A0 by required<Configuration.SerialAddressConfiguration>()
val A1 by required<Configuration.SerialAddressConfiguration>()
val rShunt by required<Double>("resistor shunt")
val maxExpectedCurrent by optional(10.0, "max expected current")
}
}
Confguration.kt:
package io.github.xyz.xyz
data class Configuration (val server: Server, val hardware: Hardware){
enum class SerialAddressConfiguration(val value: Int) {
GND(0),
VS(1),
SDA(2),
SCL(3)
}
data class Server(val port: Int, val dbFile: String)
data class Hardware(val A0: SerialAddressConfiguration, val A1: SerialAddressConfiguration, val rShunt: Double, val maxExpectedCurrent: Double)
}
Sample hocon file:
configuration = {
server = {
port = 80
database file = "data.db"
}
hardware = {
resistor shunt = 0.005
max expected current = 10.0
A0 = "GND"
A1 = "GND"
}
}
2. Error occurred:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: com.uchuhimo.konf.NameConflictException: item root cannot be added
at com.uchuhimo.konf.BaseConfig.addItem(BaseConfig.kt:530)
at com.uchuhimo.konf.RequiredConfigProperty.provideDelegate(Config.kt:399)
at io.github.xyz.xyz.MainKt.<clinit>(Main.kt:53)
3. Expected behavior:
Should cast the spec to the Configuration data class. I did not cast to data class directly because configuration hocon file has spaces in between the keys which cannot be casted to standard kotlin as variables with spaces donot exist :(
Does Konf support top-level lists in, for example, YAML files?
-
key: value
otherkey: value
-
key: value
otherkey: value
I couldn't find any documentation on this specific use-case - in my case, I can't modify the file to add a root key. Any chance of support for this?
Exception in thread "main" com.uchuhimo.konf.source.LoadException: fail to load web.host
at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:748)
at com.uchuhimo.konf.source.SourceKt$load$$inlined$apply$lambda$1.invoke(Source.kt:756)
at com.uchuhimo.konf.source.SourceKt$load$$inlined$apply$lambda$1.invoke(Source.kt)
at com.uchuhimo.konf.BaseConfig.lock(BaseConfig.kt:62)
at com.uchuhimo.konf.source.SourceKt.load(Source.kt:754)
at com.uchuhimo.konf.BaseConfig.addSource(BaseConfig.kt:464)
at com.uchuhimo.konf.Config$DefaultImpls.withSource(Config.kt:249)
at com.uchuhimo.konf.BaseConfig.withSource(BaseConfig.kt:47)
at com.uchuhimo.konf.source.Loader.resource(Loader.kt:264)
at com.xxxx.data.server.ConfigKt.classpath(Config.kt:36)
at com.xxxx.data.server.Configuration.load(Config.kt:57)
at com.xxxx.data.server.ApplicationKt.main(Application.kt:7)
Caused by: com.uchuhimo.konf.source.NoSuchPathException: cannot find path "host" in source [type: value, url: file:/home/icode/projects/data-center/server/build/resources/main/config.yml, resource: config.yml]
at com.uchuhimo.konf.source.base.ValueSource.getOrNull(ValueSource.kt:61)
at com.uchuhimo.konf.source.base.MapSource.getOrNull(MapSource.kt:50)
at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:734)
... 11 more
please ignore not exist file, and ignore config file not exist key
When you try to retrieve a "leaf spec", its innerSpecs
is not empty. This behavior is unexpected because the spec doesn't have any nested specs declared, and therefore it shouldn't have any innerSpecs
.
You can run the code below to reproduce the issue:
fun main() {
check(Service.Backend.Login.innerSpecs.isEmpty()) // innerSpecs is empty as expected
check(Service["service.backend.login"].innerSpecs.isEmpty())
{ "innerSpecs should be empty because the Login spec doesn't have any nested specs" }
}
// From the example in the README file
object Service : ConfigSpec() {
object Backend : ConfigSpec() {
object Login : ConfigSpec() { // Our "leaf spec"
val user by optional("admin")
}
}
}
Futhermore looking at the nested tests part of ConfigSpecTestSpec
, a lot of the results seems unexpected to me, so maybe I'm just not quite understanding the API.
Example:
assertThat(spec["a.bb.inner"].items, isEmpty)
This test passes, despite the Nested.Inner
object having an item named item
declared.
In com.uchuhimo.konf.example.Config.kt the example is confusing/misleading:
This code:
val basePort by ConfigSpec("server").required<Int>()
config.lazySet(Server.port) { it[basePort] + 1 }
config.lazySet("server.port") { it[basePort] + 1 }
run {
var port by config.property(Server.port)
port = 9090
check(port == 9090)
}
Is not clear what the intent is. I doesnt use 'basePort' at all.
Here is an example that may be what the intent was
val baseSpec = ConfigSpec("server")
val basePort by baseSpec.required<Int>()
config.addSpec( baseSpec )
config.lazySet(Server.port) { it[basePort] + 1 }
config.lazySet("server.port") { it[basePort] + 1 }
run {
config[basePort] = 8000
val port by config.property(Server.port)
check( port == 8001 )
}
This definition is still valid even though it can't be used anymore:
object Server : ConfigSpec("server") {
val url = required<String>("url")
}
It seems these are no longer used anywhere so it is safe to remove the code.
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?
Please think about following example code.
object ExampleSpec : ConfigSpec("") {
private val exampleKey by required<String>()
fun loadExampleKey(): String {
val config = Config { addSpec(ExampleSpec) }
.enable(Feature.LOAD_KEYS_CASE_INSENSITIVELY)
.withSource(
Source.from.json.file(File("config.json"))
)
return config[exampleKey]
}
}
With the code above, We can load:
{
"exampleKey": "value"
}
But we cannot load with error:
Exception in thread "main" java.util.concurrent.ExecutionException: com.uchuhimo.konf.UnsetValueException: item indentStyle is unset
"example_key": "value"
It's very nice if both CamelCase and snake_case are supported, especially when we load config from multiple file type such as json and yaml.
I'm having trouble figuring out the correct syntax/configuration for from.env()
support.
I created a repo with some tests showing the TOML config file I'd like to use.
https://github.com/dtanner/konf-question
Can you help me understand what I need to do to make the test config from toml and env should produce y for optionA name pass? It's currently failing with this error:
com.uchuhimo.konf.source.LoadException: fail to load root
at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:530)
at com.uchuhimo.konf.BaseConfig.addItem(BaseConfig.kt:542)
at com.uchuhimo.konf.RequiredConfigProperty.provideDelegate(Config.kt:399)
at question.AppKt.loadConfigFromTomlAndEnv(App.kt:45)
at question.AppTest.config from toml and env should produce y for optionA name(AppTest.kt:29)
....
com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: com.uchuhimo.konf.source.ObjectMappingException: unable to map source {appconfig={outputs={optiona={name=y}}}} in [type: system-environment] to value of type AppConfig
at com.uchuhimo.konf.source.SourceKt.toValue(Source.kt:834)
at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:523)
... 55 more
Caused by: com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class question.AppConfig] value failed for JSON property outputs due to missing (therefore NULL) value for creator parameter outputs which is a non-nullable type
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: question.AppConfig["outputs"])
It would be nice to have value validation implemented. My idea: required
, optional
should have two more lambda arguments, which are both optional:
validIf
parameter to etc. with following definition: (value : T) -> Boolean
invalidValueLazyMessage
with definition: (value : T) -> String?
Usage example:
object ExampleConfig : ConfigSpec() {
val foo by required<Int>(validIf = { it in (0..42) }, invalidValueLazyMessage = { "foo should be between 0 and 42, but is $it" })
}
If validIf
returns false, konf throws an exception with optional message evaluated from invalidValueLazyMessage
.
I was trying to use konf to manage my config files and migrations, together with default config.
It looks like konf can't handle, when file:
The stacktrace for empty YAML file:
Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method com.uchuhimo.konf.source.yaml.YamlConstructor$1.construct, parameter node
at com.uchuhimo.konf.source.yaml.YamlConstructor$1.construct(YamlProvider.kt)
at org.yaml.snakeyaml.constructor.BaseConstructor.getSingleData(BaseConstructor.java:151)
at org.yaml.snakeyaml.Yaml.loadFromReader(Yaml.java:525)
at org.yaml.snakeyaml.Yaml.load(Yaml.java:453)
at com.uchuhimo.konf.source.yaml.YamlProvider.inputStream(YamlProvider.kt:45)
at com.uchuhimo.konf.source.Provider$DefaultImpls.file(Provider.kt:84)
at com.uchuhimo.konf.source.yaml.YamlProvider.file(YamlProvider.kt:37)
at com.uchuhimo.konf.source.Loader.file(Loader.kt:81)
at com.uchuhimo.konf.source.Loader.file$default(Loader.kt:80)
at dev.bloodstone.konf_test.MainKt.main(Main.kt:24)
Adding dummy-value:
to config, causes it to load.
I don't know how important is handling of such edge-case, since it looks like konf is not made for my use case, and such things shouldn't happen under normal circuimstances. But I thought I'd report it anyway.
Hi - trying out Konf
and seeing this behaviour:
object ServicingConfig : ConfigSpec("servicing") {
val baseURL by required<String>("baseURL")
}
val config = Config {
addSpec(ServicingConfig)
}.withSource(
Source.from.hocon.resource("application.conf") +
Source.from.env()
)
with application.conf
:
servicing {
baseURL = "https://service/api"
}
the result is that config
is empty. However, if I remove Source.from.env()
it works as expected. My expectation was that I can override variables set in the hocon config with environmental variables (on a per environment basis for example), the absence of an environmental variable won't displace the defined variable in HOCON. Is that an unrealistic expectation?
Somehow from version 0.15.x and above, in my gradle project somehow it fails to compile due to missing dependencies of com.uchuhimo.*, e.g:
{code}
Task :compileKotlin FAILED
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (5, 12): Unresolved reference: uchuhimo
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (6, 12): Unresolved reference: uchuhimo
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (7, 12): Unresolved reference: uchuhimo
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (11, 26): Unresolved reference: Config
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (13, 24): Unresolved reference: ConfigSpec
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (28, 33): Unresolved reference: optional
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (29, 41): Unresolved reference: required
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (30, 37): Unresolved reference: optional
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (31, 39): Unresolved reference: required
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (32, 36): Unresolved reference: optional
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (49, 30): Unresolved reference: Config
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (50, 21): Unresolved reference: addSpec
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (53, 26): Unresolved reference: ConfigException
e: /Users/oalsafi/Private/Projects/kafka-consumer-lag-monitoring/src/main/kotlin/com/omarsmak/kafka/consumer/lag/monitoring/config/KafkaConsumerLagClientConfig.kt: (65, 22): Unresolved reference: ConfigException
FAILURE: Build failed with an exception.
Compilation error. See log for more details
Thanks
I would like to implement a lighter/better/simpler version of Spring cloud-config.
The concept is you have a 'bootstrap' loader which configures the source of further configurations.
This bootstrap load then could specify a URL or other source in which to read the rest of the configurations.
This can be done currently in 2 steps, first create a Config for the bootstrap which would have the a configuration specifying the Source/Loader to load. Then parse that and create a new Config using your ConfigSpec and add the .withSourceFrom.xxxx statements from the bootstrap process.
This was the rational for the previous pull request, so a URL could include the format making the bootstrap able to use a config file in any format easily.
Once a bootstrap is working then I am working on mini-server that will server config requests ... that has a bit more complexity in it as it needs to server to many clients and to pull out of the config files sub-trees of configurations. Not sure how I plan on doing that yet as the ConfigSpec would not be in the server side only the loader, but its useful for it to be able to parse the files fully so it can pull out subtrees ... which requires a kind of ConfigSpec for that (or a bunch more low level code).
Ultimately Im aiming at kotlin-script based DSL that could be dynamically loaded.
But first step, a consistent config format to specify config sources and/or loaders.
Any suggestions welcome.
I have also implemented a basic s3 Loader essentially this:
fun getS3ObjectAsStream( s3url: String , region: String ) = ...
...
fun Loader.s3(s3url: String , region: String): Config =
config.withSource(provider.fromInputStream(getS3ObjectAsStream(s3url,region=region)))
...
val configSpec = Config { addSpec(MyConfig) }
.withSourceFrom.properties.s3("s3://mys3bucket/test/config.properties" , region="us-east-1" )
The goal being to put protected config files in S3 and then use the running app's authentication to load from them. Ultimately the above would be configured in the bootstrap, possibly augmented with system properties. In this case Im using it from within a AWS Lambda function which has limited access to the environment so it must get everything from outside or from env variables.
I am working on enhancing the URL provider to accept the s3 scheme, doing it the 'normal' way of adding new protocols to Java doesn't work in Lambda due to the security manager so it need be done with custom code. (aka the getS3ObjectAsStream())
Also, thank you ! I have been looking for finding or writing a good config system and have had many dead-ends that were just not quite right -- (including my own) -- and finally found konf !! This is by far the cleanest and most generally useful config library Ive found yet so am basing my future apps on it.
given
database {
name = "servicedb"
hostname = "db"
port = 5432
}
flyway {
url= "jdbc:postgresql://${database.hostname}:${database.port:-5432}/${database.name}"
}
it fails with
Caused by: com.uchuhimo.konf.source.WrongTypeException: source 5432 in [type: HOCON, url: file:/xxx/server/build/resources/main/application.conf, resource: application.conf] has type Integer rather than String
at com.uchuhimo.konf.source.SourceKt.asValueOf(Source.kt:448) ~[konf-core-0.22.1.jar:na]
at com.uchuhimo.konf.source.TreeLookup.lookup(Source.kt:407) ~[konf-core-0.22.1.jar:na]
at org.apache.commons.text.lookup.InterpolatorStringLookup.lookup(InterpolatorStringLookup.java:151) ~[commons-text-1.8.jar:1.8]
is it possible to do this?
Jitpack is timing out. This has happened twice. Once right now and once yesterday.
Hi,
is there any way for creating a default configuration file. For example upon startup. I know there are functions like config.toJson('config.json')
, but those functions only output the current values within the config object.
Imagine this situation:
import com.uchuhimo.konf.Config
import com.uchuhimo.konf.ConfigSpec
object ServerSpec : ConfigSpec() {
val host by optional("0.0.0.0")
val port by required<Int>()
}
object ClientSpec : ConfigSpec() {
val test by required<String>(description = "some test value")
}
val config = Config()
config.addSpec(ServerSpec)
config.addSpec(ClientSpec)
config.toJson.toFile("config.json")
The output to config.json will be
{
"server" : {
"host" : "0.0.0.0"
}
}
instead of a fully valid config.json
file with all required parameters.
It would make a devs life much easier, when the generated config file would contain the required parameters as well, because then you can simply check upon startup if the config file exists / contains all required fields, if not it outputs the standard values with a hint to update those.
I'm finding that toValue
fails with a layered configs.
Test code:
package st.legevent
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import com.uchuhimo.konf.BaseConfig
import com.uchuhimo.konf.Config
import com.uchuhimo.konf.source.LoadException
import com.uchuhimo.konf.source.yaml
import com.uchuhimo.konf.toValue
import org.junit.Test
class TestConfigTestReport {
@Test
fun succeed1() {
val config = Config()
.from.yaml.resource("testKonf.yml")
loadAndPrint(config)
}
@Test
fun succeed2() {
val config = BaseConfig(
mapper = ObjectMapper(/*YAMLFactory()*/)
.registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
)
.from.yaml.resource("testKonf.yml")
loadAndPrint(config)
}
@Test(expected = LoadException::class)
fun fail1() {
val config = Config()
.from.yaml.resource("testKonf.yml")
.from.yaml.file(
System.getenv("SERVICE_CONFIG") ?: "/opt/legacy-event-service/conf/legacy-event-service.yml",
true
)
loadAndPrint(config)
}
@Test(expected = LoadException::class)
fun fail2() {
val config = BaseConfig(
mapper = ObjectMapper(/*YAMLFactory()*/)
.registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
)
.from.yaml.resource("testKonf.yml")
.from.yaml.file(
System.getenv("SERVICE_CONFIG") ?: "/opt/legacy-event-service/conf/legacy-event-service.yml",
true
)
loadAndPrint(config)
}
@Test(expected = LoadException::class)
fun fail3() {
val config = BaseConfig(
mapper = ObjectMapper(/*YAMLFactory()*/)
.registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
)
.from.yaml.resource("testKonf.yml")
.from.env()
loadAndPrint(config)
}
@Test(expected = LoadException::class)
fun fail4() {
val config = Config()
.from.yaml.resource("testKonf.yml")
.from.env()
loadAndPrint(config)
}
@Test(expected = LoadException::class)
fun fail5() {
val config = BaseConfig(
mapper = ObjectMapper(/*YAMLFactory()*/)
.registerKotlinModule()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
)
.from.yaml.resource("testKonf.yml")
.from.systemProperties()
loadAndPrint(config)
}
@Test(expected = LoadException::class)
fun fail6() {
val config = Config()
.from.yaml.resource("testKonf.yml")
.from.systemProperties()
loadAndPrint(config)
}
}
private fun loadAndPrint(config: Config) {
val db = config.toValue<ConfigTestReport>()
println(db)
}
data class ConfigTestReport(val db: Map<String, *>)
Contents of src/test/resources/testKonf.yml:
db:
driverClassName: org.h2.Driver
url: 'jdbc:h2:mem:db;DB_CLOSE_DELAY=-1'
Given the following custom StdSerializer
, it's not possible to serialize my custom object as a String
without getting errors. An json object is expected because it needs to be able to convert to a Map
. So far it doesn't seem to affect deserialization.
The following example isn't supported: (example error: https://paste.ubuntu.com/p/MhTV9333Qr/)
internal class TextSerializer : StdSerializer<Text>(Text::class.java) {
override fun serialize(value: Text, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeString(value.toPlain())
}
}
This works:
internal class TextSerializer : StdSerializer<Text>(Text::class.java) {
override fun serialize(value: Text, gen: JsonGenerator, provider: SerializerProvider) {
gen.writeStartObject()
gen.writeStringField("text", value.toPlain())
gen.writeEndObject()
}
}
Sorry for a question in the issues but I don't find a place where I can ask it.
Example toml config:
[storage.local_s3]
type = "s3"
host = "http://localhost:8000"
access_key = "123456789"
secret_key = "abcdef"
[storage.local_fs]
type = "fs"
path = "/var/lib/storage"
sealed class Storage {
class FS(val path: String) : Storage
class S3(val host: String, val accessKey: String, val secretKey: String) : Storage
}
Is it possible that storages
config item would contain a map with correspondingStorage
objects?
Maybe there is an extension point in the library to do that trick but I cannot find it. So what I want is something like this:
fun storageTypeMapper(source: Source): Storage {
val type = source.get("type").toText()
return when(type) {
"fs" -> Storage.FS(...)
"s3" -> Storage.S3(...)
else -> throw SourceException("...")
}
}
class MyAppSpec : ConfigSpec() {
val storages by required<Map<String, Storage>>("storage", typeMapper = storageTypeMapper))
}
Hello,
Say I have the following set of existing environment variables within my infrastructure:
DATABASE=foo.example.com
DATABASE_USER=john
DATABASE_PASS=doe
I want to, without modifying them, be able to resolve these variables from both YAML file and environment variables, with latter having precedence.
It seems that such naming is discouraged by konf
.
I tried the following:
object Root : ConfigSpec("") {
val DATABASE by required<String>()
val DATABASE_USER by required<String>()
val DATABASE_PASS by required<String>()
}
Config {
addSpec(Root)
}
.from.yaml.resource("server.yml")
.from.env()
.from.systemProperties()
With YAML file having similar structure to the above:
DATABASE: foo.example.com
DATABASE_USER: john
DATABASE_PASS: doe
Sadly, the moment DATABASE_PASS
or similar variable appears in the env, I get the following exception:
Exception in thread "main" com.uchuhimo.konf.source.LoadException: fail to load DATABASE
at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:519)
at com.uchuhimo.konf.source.SourceKt$load$1.invoke(Source.kt:532)
at com.uchuhimo.konf.source.SourceKt$load$1.invoke(Source.kt)
at com.uchuhimo.konf.BaseConfig.lock(BaseConfig.kt:65)
at com.uchuhimo.konf.source.SourceKt.load(Source.kt:530)
at com.uchuhimo.konf.BaseConfig.withSource(BaseConfig.kt:585)
at com.uchuhimo.konf.source.DefaultLoaders.env(DefaultLoaders.kt:118)
at example.server.ServerSpecKt.getApplicationConfig(ServerSpec.kt:41)
at example.StarterAppKt.initializeApplicationContext(StarterApp.kt:43)
at example.StarterAppKt.main(StarterApp.kt:68)
at example.StarterAppKt.main(StarterApp.kt)
Caused by: com.uchuhimo.konf.source.ObjectMappingException: unable to map source com.uchuhimo.konf.ContainerNode@33c2bd in [type: system-environment] to value of type String
at com.uchuhimo.konf.source.SourceKt.toValue(Source.kt:820)
at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:512)
... 10 more
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
at [Source: UNKNOWN; line: -1, column: -1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1139)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1093)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:63)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:10)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3985)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2343)
at com.uchuhimo.konf.source.SourceKt.toValue(Source.kt:815)
... 11 more
Is there a mode to rely on simple naming without inferred nesting?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.