Search

Dark theme | Light theme
Showing posts with label GroovyGoodness:TOML. Show all posts
Showing posts with label GroovyGoodness:TOML. Show all posts

July 1, 2022

Groovy Goodness: Creating TOML Configuration With TomlBuilder

Groovy 4 introduced support for TOML configuration file. In a previous post we already learned how we can parse TOML content. In this post we will see we can use a builder syntax to create TOML content. We need the class TomlBuilder and then define our structure using a nice builder DSL. The DSL is comparable to create JSON using the JsonBuilder. The names of the nodes in the DSL structure will be the names of the properties. Nodes within nodes will result in concatenated property names with the name of each node separated by a dot (.). We can also use collections as arguments and those will translated to TOML arrays. A collection can optionally be followed by a closure that processes each item in the collection to generate the content for the TOML array.

In the following example we use the builder syntax to create what we want in our TOML content. Using the toString method we get the TOML data as string:

import groovy.toml.TomlBuilder

// Helper record class to store "server" properties.
record Server(String env, String host, int port) {}

// Create Tomlbuilder.
def toml = new TomlBuilder()

// Define structure.
toml {
    // Use closure to group.
    application {
        name "Groovy TOML"
        version "1.0.0"
    }

    // Use closures to define levels.
    users {
        env {
           enabled true
        }
        acc {
            enabled false
        }
    }
         
    // Use maps
    debug(enabled: true)  
    
    // We can use collections
    ports([80, 443]) 
    
    // Convert data with closure applied for each item in collection.
    servers([new Server("dev", "localhost", 8080), 
             new Server("uat", "cloud-acc", 80)], { server -> 
        env server.env 
        host server.host 
        port server.port 
    }) 
}

assert toml.toString() == """\
application.name = 'Groovy TOML'
application.version = '1.0.0'
users.env.enabled = true
users.acc.enabled = false
debug.enabled = true
ports = [80, 443]
servers = [{env = 'dev', host = 'localhost', port = 8080}, {env = 'uat', host = 'cloud-acc', port = 80}]
"""

// In order to write to a writer we could use:
// def sw = new StringWriter()
// toml.writeTo(sw)
// def content = sw.toString()

Instead of using the builder DSL syntax we can also use a Map with all data we want to transform to TOML data:

import groovy.toml.TomlBuilder

def instant = Instant.ofEpochSecond(1656487920)
def clock = Clock.fixed(instant, ZoneOffset.UTC)

def config = [
    application: [
      name: "Groovy TOML",
      version: "1.0.0"
    ],
    users: [
        dev: [enabled: true],
        uat: [enabled: false]
    ],
    ports: [80, 443],
    debug: [
        enabled: false
    ],
    build: [
        jdk: 'openjdk version "17.0.3" 2022-04-19',
        time: ZonedDateTime.now(clock).dateTimeString
   ],
   servers: [
       [env: "dev", host: "localhost", port: 8080],
       [env: "uat", host: "cloud-acc", port: 80]
   ]
]

// Create TomlBuilder
def toml = new TomlBuilder()

// Use data defined in the Map.
toml config

assert toml.toString() == """\
application.name = 'Groovy TOML'
application.version = '1.0.0'
users.dev.enabled = true
users.uat.enabled = false
ports = [80, 443]
debug.enabled = false
build.jdk = 'openjdk version "17.0.3" 2022-04-19'
build.time = '2022-06-29T07:32:00Z'
servers = [{env = 'dev', host = 'localhost', port = 8080}, {env = 'uat', host = 'cloud-acc', port = 80}]
"""

Written with Groovy 4.0.3.

June 29, 2022

Groovy Goodness: Reading TOML Configuration

Since Groovy 4 we can parse TOML configuration data into a Map. Once the TOML data is transformed into the Map we can use all possibilities in Groovy to lookup keys and their values in maps. For example we can use GPath expressions to easily get the value of a (nested) key. To parse TOML configuration data we must use the TomlSlurper class that is in the groovy.toml package. We can use the parse method when we have a file, reader or stream with our configuration. To parse a String value with TOML configuration we use the parseText method.

In the following example we define our configuration using TOML syntax and use the parseText method to transform it into a Map. We use different ways with GPath expressions to read the configuration data:

import groovy.toml.TomlSlurper

// Configuration in TOML syntax.
def config = '''
application.name = "Groovy TOML"
application.version = "1.0.0"
application.ports = [80, 443]

# Set to true for debugging
debug.enabled = false

[build]
jdk = 'openjdk version "17.0.3" 2022-04-19'
time = "2022-06-29T07:32:00Z"

[[servers]]
name = "dev"
host = "localhost"
port = 8080

[[servers]]
name = "uat"
host = "cloud-acc"
port = 80
'''

// We get back a Map with all configuration.
def toml = new TomlSlurper().parseText(config)
// To read data from files, readers or streams we can use overloaded
// versions of the method TomlSlurper#parse.

// We can reference the properties using GPath expressions.
assert toml.application.name == "Groovy TOML"
assert toml.application.version == "1.0.0"
// TOML array is transformed to ArrayList.
assert toml.application.ports == [80, 443]
assert toml.application.ports.class == java.util.ArrayList
assert toml.application == [name: "Groovy TOML", version: "1.0.0", ports: [80, 443]]

// TOML boolean is transformed to boolean.
assert !toml.debug.enabled

assert toml.build.jdk == /openjdk version "17.0.3" 2022-04-19/
assert toml.build.time == "2022-06-29T07:32:00Z"
// Dates are not parsed, but we get them as String value.
assert toml.build.time.class == java.lang.String

// Array of tables in TOML are also supported by the TomlSlurper.
assert toml.servers.size() == 2

def developmentConfig = toml.servers.find { s -> s.name == "dev" }
assert developmentConfig.host == "localhost"
assert developmentConfig.port == 8080

def uatConfig = toml.servers.find { s -> s.name == "uat" }
assert uatConfig.host == "cloud-acc"
assert uatConfig.port == 80

assert toml.servers*.name == ["dev", "uat"]

Written with Groovy 4.0.3.