A Guide to TOML in Golang
Introduction
TOML short for Tom’s Obvious, Minimal Language is a configuration file format that is easy to read due to its minimal syntax. It is a popular alternative to YAML, INI and JSON for configuration files and data exchange formats.
TOML syntax
TOML syntax consists of key-value pairs, tables and arrays. Tables are used to group related data. []
is used to define a table and can be netsted within other tables to create a hierachy of data. Key-value pairs are used to define the data within a table. =
is used to assign a value to a key. Finally, arrays. Arrays are used to store multiple values of the same type. []
is used to define an array and ,
is used to separate values.
In the above example, we have defined a table called server
with a nested table called development
. We have also defined an array called hosts
within the development
table. The hosts
array contains three values. The first two are strings and the last one is an array of strings.
TOML in Go
To use TOML in Go, there are two major packages that you can use. The first is github.com/BurntSushi/toml
and the second is github.com/pelletier/go-toml
. Both serve the same purpose but have different APIs the former has an easier to implement APIs while the later is good for sophisticated usecases. In this article we will explore Both packages and see how they can be used in Go.
pelletier/go-toml
This package has the same behaviour as the stdlib encoding/json
package. It has a Marshal
and Unmarshal
function that can be used to convert Go data structures to and from TOML. It also has better support for arrays, nested tables and human readable error messages.
Marshal
The Marshal
function takes a Go data structure and converts it to TOML. It returns a byte slice and an error. The error is returned if the data structure cannot be converted to TOML.
In this example, we have a Config
struct that contains a nested struct called Database
. We have defined some data for the Database
struct and then encoded it to TOML format using the Marshal
function. Finally, we write the encoded data to a file called config.toml
.
Unmarshal
Unmarshal
is takes a byte slice and converts it to a Go data structure. In this case, the byte slice is a TOML file.
The file is opened and the data is read into a slice since the Unmarshal
function function takes a byte slice and a pointer to a Go data structure.
The data is then decoded into the Config
struct using the Unmarshal
function.
The output would be localhost
.
Other functions
The package also implements other functions that like Marshal
and Unmarshal
. These functions are Encode
and Decode
. The Tree
is also used to convert to a Go data structure without having to define a struct.
This code will achieve the same result as the previous example.
BurntSushi/toml
This package is far simpler and easier to use than the pelletier/go-toml
package. It has inbuilt reflection and can perform both encoding and decoding.
Decoding TOML
To convert TOML to Go, we use the toml.Decode
function. This function takes a byte slice and a pointer to a Go data structure. The byte slice is the TOML data and the pointer is the Go data structure that the TOML data will be decoded into.
The DecodeFile
function is used to read the TOML data from a file. The Decode
function can be used to read the TOML data from a byte slice.
Encoding TOML
To transform a Go data structure to TOML, we use toml.NewEncoder
to create an encoder which takes an io.Writer
as an argument. A byte slice is then written to the io.Writer
using the Encode
function.
Using TextMarshaler
and TextUnmarshaler
These interfaces can be used to implement custom encoding and decoding. The TextMarshaler
interface is used to implement custom encoding and the TextUnmarshaler
interface is used to implement custom decoding.
In this example, we have created a custom type duration
which is a wrapper around time.Duration
. We have implemented the TextMarshaler
and TextUnmarshaler
interfaces to encode and decode the custom type. The MarshalText
function is used to encode the custom type and the UnmarshalText
function is used to decode the custom type.
Conclusion
In this article, we have looked at how to use the pelletier/go-toml
and BurntSushi/toml
packages to encode and decode TOML data. We have also looked at how to implement custom encoding and decoding using the TextMarshaler
and TextUnmarshaler
interfaces.