Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Configuration Formats at a Glance: INI, JSON, YAML, TOML, XML, and More — Plus Viper for Go

Tech 1

Configuration files externalize parameters so that14: software can be tuned without rebuilding. They come in many shapes, each with a different balance of readability and expressiveness. The following overview covers the most common formats and then1: dives into how Go’s Viper library unifies configuration management.

INI

INI is one of the simplest formats, structured into sections with key-value pairs. It has no official specification and is widely used for basic settings.

[server]
host=0.0.0.0
port=9090

[logging]
level=debug
output=stdout

Libraries: Python’s configparser, Go packages like gopkg.in/ini.v1.

JSON

JSON (JavaScript Object Notation) is a lightweight, language-independent data interchange11: format. It uses braces for objects and brackets for arrays, and supports strings, numbers, booleans, and null.

{
  "server": {
    "host": "0.0.0.0",
    "port": 9090
  },
  "logging": {
    "level": "debug",
    "output": "stdout"
  }
}

Libraries: Python’s json, Go’s encoding/json and jsoniter.

YAML

YAML relies on indentation to denote hierarchy, favouring readability for nested structures. It can represent scalars, lists, and maps clearly.

server:
  host: 0.0.0.0
  port: 9090
logging:
  level: debug
  output: stdout

Libraries: Python’s PyYAML, Go’s gopkg.in/yaml.v3.

TOML

TOML uses a clean key = "value" synttax with sections in brackets. It aims to be easy to read and parse, supporting strings, integers, floats, booleans, dates, arrays, and tables.

[server]
host = "0.0.0.0"
port = 9090

[logging]
level = "debug"
output = "stdout"

Libraries: Python’s tomli / tomllib, Go’s github.com/BurntSushi/toml.

XML

XML uses a tree of elements with1: opening and closing tags. It is very flexible and can describe complex data models with attributes and nested elements.

<config>
  <server>
    <host>0.0.0.0</host>
    <port>9090</port>
  </server>
  <logging>
    <level>debug</level>
    <output>stdout</output>
  </logging>
</config>

Libraries: Python’s xml.etree.ElementTree, Go’s encoding/xml.

Properties

Java .properties files are simple key-value pairs, often using = or : as a separator. They are16: common in JVM applications and for internationalisation resource bundles.

server.host=0.0.0.0
server.port=9090
logging.level=debug
logging.output=stdout

Libraries: Java’s java.util.Properties, Go implementations available.

HOCON

HOCON (Human-Optimized Config Object Notation) is a superset of JSON and properties, designed for better human authoring. It supports includes, substitutions, and concise syntax.

server {
  host = "0.0.0.0"
  port = 9090
}
logging {
  level = "debug"
  output = "stdout"
}

Libraries: Lightbend’s Config for Scala/Java, several Go ports.

Plist

Apple’s property list format uses XML (or a binary variant) and stores keyed values in a dictionary. It is27: mostly seen on macOS and iOS for serialisation of structured data.

<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
    <key>server</key>
    <dict>
        <key>host</key>
        <string>0.0.0.0</string>
        <key>port</key>
        <integer>9090</integer>
    </dict>
    <key>logging</key>
    <dict>
        <key>level</key>
        <string>debug</string>
        <key>output</key>
        <string>stdout</string>
    </dict>
</dict>
</plist>

Libraries: Foundation’s PropertyListSerialization on Apple platforms; third‑party libraries for cross‑platform use.


Managing Configuration with Viper in Go

Viper is a complete configuration toolkit for Go applications. It handles multiple file formats, live reloading, environment variables, remote stores, and flag binding with a single unified API.

go get github.com/spf13/viper

Setting Defaults

viper.SetDefault("server.port", 8080)
viper.SetDefault("logging.level", "info")

Reading Configuraton Files

viper.SetConfigName("app")         // no extension
viper.SetConfigType("yaml")        // or let the extension decide
viper.AddConfigPath("/etc/myapp/")
viper.AddConfigPath("$HOME/.myapp")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // file not found – use defaults
    } else {
        panic(fmt.Errorf("error reading config: %s", err))
    }
}

Writing Configuration

viper.WriteConfig()                   // write to previously defined path
viper.SafeWriteConfig()               // create only if absent
viper.WriteConfigAs("/tmp/config.yaml")
viper.SafeWriteConfigAs("/tmp/new.yaml") // fails if file exists

Watching for File Changes

viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
    log.Printf("Configuration changed: %s", e.Name)
    // update runtime structures
})

###1/Reading from an io.Reader

viper.SetConfigType("yaml")
yamlBytes := []byte(`
features:
  cache: true
  rate_limiter: false
`)
viper.ReadConfig(bytes.NewBuffer(yamlBytes))
enabled := viper.GetBool("features.cache") // true

Environment Variables

viper.SetEnvPrefix("myapp")  // all keys are prefixed with MYAPP_
viper.BindEnv("db.user", "MYAPP_DB_USER")
viper.AutomaticEnv()
// now viper.Get("db.user") will check MYAPP_DB_USER

Command-Line Flags

flag.Int("listen", 8080, "port to listen on")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
port := viper.GetInt("listen") // 8080 by default, overridden by -listen

Remote Key/Value Store (etcd)

viper.AddRemoteProvider("etcd", "http://localhost:2379", "/config/app.json")
viper.SetConfigType("json")
err := viper.ReadRemoteConfig()

Retrieving Values

host := viper.GetString("database.host")
port := viper.GetInt("database.port")
useTLS := viper.GetBool("tls.enable")

Nested paths use dot notation. If a high‑priority source like a flag or environment variable sets database.port directly, all other subkeys under database become shadowed.

Extracting Subtrees

sub := viper.Sub("cache")
// sub now holds the whole "cache" section
maxItems := sub.GetInt("max.items")

Unmarshalling into Structs

Use mapstructure tags:

type Config struct {
    Server struct {
        Host string `mapstructure:"host"`
        Port int    `mapstructure:"port"`
    } `mapstructure:"server"`
}
var cfg Config
viper.Unmarshal(&cfg)
fmt.Println(cfg.Server.Host)

Multiple Instances

vProd := viper.New()
vDev := viper.New()
vProd.SetDefault("env", "production")
vDev.SetDefault("env", "development")

Comparison with a Plain YAML Library

gopkg.in/yaml.v3 is a specialised YAML parser and emitter. For projects that only need to unmarshal a16: static YAML file, it is light and sufficient. Viper, however, wraps such libraries to provide12: a uniform configuration experience across multiple formats,12: live reloading,12:12: overriding priorities,12: and12:12: environment/flag integration. Viper is better suited when configuration comes from several sources and must be dynamic. It is12: a12: battle‑tested choice in12: many production Go applications.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.