Top Related Projects
TOML parser for Golang with reflection.
Go configuration with fangs
YAML support for the Go language.
A Go port of Ruby's dotenv library (Loads environment variables from .env files)
Golang library for managing configuration data from environment variables
Mergo: merging Go structs and maps since 2013
Quick Overview
pelletier/go-toml is a Go library for parsing and manipulating TOML (Tom's Obvious, Minimal Language) files. It provides a robust and efficient implementation for working with TOML data in Go applications, supporting both reading and writing TOML files.
Pros
- Full support for TOML v1.0.0 specification
- High performance and memory efficiency
- Extensive API for querying and modifying TOML data
- Well-maintained with regular updates and bug fixes
Cons
- Learning curve for complex operations
- Limited support for older TOML versions
- Occasional breaking changes between major versions
Code Examples
- Parsing a TOML file:
config, err := toml.LoadFile("config.toml")
if err != nil {
log.Fatal(err)
}
fmt.Println(config.Get("database.server").(string))
- Creating and writing a TOML file:
newConfig := toml.NewTable()
newConfig.Set("name", "John Doe")
newConfig.Set("age", 30)
f, err := os.Create("output.toml")
if err != nil {
log.Fatal(err)
}
defer f.Close()
encoder := toml.NewEncoder(f)
err = encoder.Encode(newConfig)
if err != nil {
log.Fatal(err)
}
- Querying nested TOML data:
tree, _ := toml.Load(`
[fruits]
[fruits.apple]
color = "red"
taste = "sweet"
`)
apple := tree.Get("fruits.apple").(*toml.Tree)
fmt.Println(apple.Get("color").(string)) // Output: red
Getting Started
To use pelletier/go-toml in your Go project:
-
Install the library:
go get github.com/pelletier/go-toml/v2
-
Import it in your Go code:
import "github.com/pelletier/go-toml/v2"
-
Start using the library to parse or create TOML data:
data, err := toml.Load(` [server] host = "localhost" port = 8080 `) if err != nil { log.Fatal(err) } fmt.Println(data.Get("server.port").(int64))
Competitor Comparisons
TOML parser for Golang with reflection.
Pros of toml
- More established and widely used in the Go community
- Extensive documentation and examples available
- Supports both encoding and decoding of TOML data
Cons of toml
- Slightly slower performance compared to go-toml
- Less frequent updates and maintenance
Code Comparison
go-toml:
config, err := toml.LoadFile("config.toml")
if err != nil {
log.Fatal(err)
}
value := config.Get("database.server").(string)
toml:
var config struct {
Database struct {
Server string `toml:"server"`
} `toml:"database"`
}
_, err := toml.DecodeFile("config.toml", &config)
if err != nil {
log.Fatal(err)
}
value := config.Database.Server
Both libraries offer similar functionality for parsing TOML files, but go-toml provides a more flexible API for accessing values, while toml uses struct tags for mapping TOML data to Go structs.
go-toml offers additional features like querying with dotted keys and modifying the parsed tree, making it more versatile for complex configurations. However, toml's approach of using struct tags can lead to more type-safe and idiomatic Go code.
Ultimately, the choice between these libraries depends on specific project requirements, performance needs, and personal preference for API design.
Go configuration with fangs
Pros of Viper
- Supports multiple configuration formats (YAML, JSON, TOML, etc.)
- Offers live watching and automatic reloading of configuration files
- Provides environment variable support and command-line flag integration
Cons of Viper
- More complex setup and usage compared to go-toml
- Larger dependency footprint
- May be overkill for simple configuration needs
Code Comparison
go-toml:
config, _ := toml.LoadFile("config.toml")
value := config.Get("database.host").(string)
Viper:
viper.SetConfigFile("config.toml")
viper.ReadInConfig()
value := viper.GetString("database.host")
Summary
Viper offers a more comprehensive configuration management solution with support for multiple formats and dynamic reloading. However, it comes with increased complexity and a larger footprint. go-toml is simpler and more lightweight, focusing specifically on TOML parsing. Choose Viper for more advanced configuration needs and go-toml for straightforward TOML handling in Go projects.
YAML support for the Go language.
Pros of yaml
- More widely used and established in the Go community
- Supports both YAML 1.1 and 1.2 specifications
- Offers more advanced features like custom tags and type-safe unmarshaling
Cons of yaml
- Generally slower performance compared to go-toml
- More complex API, which can lead to a steeper learning curve
- Larger codebase and dependencies
Code Comparison
yaml:
type Config struct {
Name string `yaml:"name"`
Age int `yaml:"age"`
}
var cfg Config
err := yaml.Unmarshal([]byte(yamlData), &cfg)
go-toml:
type Config struct {
Name string
Age int
}
var cfg Config
err := toml.Unmarshal([]byte(tomlData), &cfg)
Both libraries offer similar unmarshaling capabilities, but yaml requires explicit struct tags for field mapping, while go-toml can infer field names automatically.
yaml provides more flexibility in handling complex data structures and custom types, making it suitable for more advanced use cases. However, go-toml offers a simpler API and better performance for basic configuration needs.
The choice between these libraries depends on the specific requirements of your project, such as the complexity of your data structures, performance needs, and familiarity with YAML or TOML formats.
A Go port of Ruby's dotenv library (Loads environment variables from .env files)
Pros of godotenv
- Simpler and more focused on handling .env files
- Lightweight and easy to integrate into projects
- Follows the widely-used .env file format standard
Cons of godotenv
- Limited to .env file format, less versatile than go-toml
- Lacks advanced features like marshaling/unmarshaling structs
- No support for writing or modifying configuration files
Code Comparison
godotenv:
import "github.com/joho/godotenv"
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
go-toml:
import "github.com/pelletier/go-toml/v2"
config, err := toml.LoadFile("config.toml")
if err != nil {
log.Fatal(err)
}
Summary
godotenv is a straightforward library for loading environment variables from .env files, making it ideal for simple configuration management. go-toml, on the other hand, offers more comprehensive TOML file handling capabilities, including reading, writing, and manipulating TOML data structures. While godotenv is easier to use for basic .env file operations, go-toml provides greater flexibility and features for working with TOML-formatted configuration files.
Golang library for managing configuration data from environment variables
Pros of envconfig
- Simpler and more lightweight, focusing solely on environment variable configuration
- Directly populates struct fields, reducing boilerplate code
- Supports custom parsing for complex types
Cons of envconfig
- Limited to environment variables, less flexible for complex configurations
- Lacks support for hierarchical data structures
- No built-in file parsing capabilities
Code Comparison
envconfig:
type Config struct {
Host string `envconfig:"HOST"`
Port int `envconfig:"PORT"`
}
var c Config
err := envconfig.Process("myapp", &c)
go-toml:
type Config struct {
Host string
Port int
}
var c Config
tree, _ := toml.LoadFile("config.toml")
err := tree.Unmarshal(&c)
Key Differences
- go-toml is more versatile, supporting TOML file parsing and manipulation
- envconfig is focused on environment variables, making it simpler for specific use cases
- go-toml offers more advanced features like querying and modifying TOML data
- envconfig provides a more straightforward approach for environment-based configuration
Use Case Considerations
- Choose envconfig for projects primarily using environment variables for configuration
- Opt for go-toml when dealing with complex, hierarchical configurations or TOML files
- Consider go-toml for projects requiring both file-based and programmatic configuration management
Mergo: merging Go structs and maps since 2013
Pros of mergo
- Focuses on merging Go structs and maps, offering more specialized functionality
- Provides options for customizing merge behavior, such as overwriting or appending slices
- Supports deep merging of nested structures
Cons of mergo
- Limited to merging Go data structures, unlike go-toml's TOML parsing capabilities
- May require more manual setup for complex merging scenarios
- Less suitable for configuration file handling compared to go-toml
Code Comparison
mergo:
type Foo struct {
A string
B int
}
var src = Foo{A: "one", B: 2}
var dst = Foo{A: "two"}
mergo.Merge(&dst, src)
go-toml:
type Config struct {
Name string
Port int
}
var config Config
toml.Unmarshal([]byte(`name = "example"`), &config)
Summary
mergo is specialized for merging Go structs and maps, offering customizable merge options. go-toml focuses on parsing and manipulating TOML configuration files. While mergo provides more control over merging data structures, go-toml is better suited for handling configuration files in the TOML format. The choice between the two depends on the specific use case and data format requirements of your project.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
go-toml v2
Go library for the TOML format.
This library supports TOML v1.0.0.
Documentation
Full API, examples, and implementation notes are available in the Go documentation.
Import
import "github.com/pelletier/go-toml/v2"
See Modules.
Features
Stdlib behavior
As much as possible, this library is designed to behave similarly as the
standard library's encoding/json
.
Performance
While go-toml favors usability, it is written with performance in mind. Most operations should not be shockingly slow. See benchmarks.
Strict mode
Decoder
can be set to "strict mode", which makes it error when some parts of
the TOML document was not present in the target structure. This is a great way
to check for typos. See example in the documentation.
Contextualized errors
When most decoding errors occur, go-toml returns DecodeError
,
which contains a human readable contextualized version of the error. For
example:
1| [server]
2| path = 100
| ~~~ cannot decode TOML integer into struct field toml_test.Server.Path of type string
3| port = 50
Local date and time support
TOML supports native local date/times. It allows to represent a given
date, time, or date-time without relation to a timezone or offset. To support
this use-case, go-toml provides LocalDate
, LocalTime
, and
LocalDateTime
. Those types can be transformed to and from time.Time
,
making them convenient yet unambiguous structures for their respective TOML
representation.
Commented config
Since TOML is often used for configuration files, go-toml can emit documents annotated with comments and commented-out values. For example, it can generate the following file:
# Host IP to connect to.
host = '127.0.0.1'
# Port of the remote server.
port = 4242
# Encryption parameters (optional)
# [TLS]
# cipher = 'AEAD-AES128-GCM-SHA256'
# version = 'TLS 1.3'
Getting started
Given the following struct, let's see how to read it and write it as TOML:
type MyConfig struct {
Version int
Name string
Tags []string
}
Unmarshaling
Unmarshal
reads a TOML document and fills a Go structure with its
content. For example:
doc := `
version = 2
name = "go-toml"
tags = ["go", "toml"]
`
var cfg MyConfig
err := toml.Unmarshal([]byte(doc), &cfg)
if err != nil {
panic(err)
}
fmt.Println("version:", cfg.Version)
fmt.Println("name:", cfg.Name)
fmt.Println("tags:", cfg.Tags)
// Output:
// version: 2
// name: go-toml
// tags: [go toml]
Marshaling
Marshal
is the opposite of Unmarshal: it represents a Go structure
as a TOML document:
cfg := MyConfig{
Version: 2,
Name: "go-toml",
Tags: []string{"go", "toml"},
}
b, err := toml.Marshal(cfg)
if err != nil {
panic(err)
}
fmt.Println(string(b))
// Output:
// Version = 2
// Name = 'go-toml'
// Tags = ['go', 'toml']
Unstable API
This API does not yet follow the backward compatibility guarantees of this library. They provide early access to features that may have rough edges or an API subject to change.
Parser
Parser is the unstable API that allows iterative parsing of a TOML document at the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable.
Benchmarks
Execution time speedup compared to other Go TOML libraries:
Benchmark | go-toml v1 | BurntSushi/toml |
---|---|---|
Marshal/HugoFrontMatter-2 | 1.9x | 2.2x |
Marshal/ReferenceFile/map-2 | 1.7x | 2.1x |
Marshal/ReferenceFile/struct-2 | 2.2x | 3.0x |
Unmarshal/HugoFrontMatter-2 | 2.9x | 2.7x |
Unmarshal/ReferenceFile/map-2 | 2.6x | 2.7x |
Unmarshal/ReferenceFile/struct-2 | 4.6x | 5.1x |
See more
The table above has the results of the most common use-cases. The table below contains the results of all benchmarks, including unrealistic ones. It is provided for completeness.
Benchmark | go-toml v1 | BurntSushi/toml |
---|---|---|
Marshal/SimpleDocument/map-2 | 1.8x | 2.7x |
Marshal/SimpleDocument/struct-2 | 2.7x | 3.8x |
Unmarshal/SimpleDocument/map-2 | 3.8x | 3.0x |
Unmarshal/SimpleDocument/struct-2 | 5.6x | 4.1x |
UnmarshalDataset/example-2 | 3.0x | 3.2x |
UnmarshalDataset/code-2 | 2.3x | 2.9x |
UnmarshalDataset/twitter-2 | 2.6x | 2.7x |
UnmarshalDataset/citm_catalog-2 | 2.2x | 2.3x |
UnmarshalDataset/canada-2 | 1.8x | 1.5x |
UnmarshalDataset/config-2 | 4.1x | 2.9x |
geomean | 2.7x | 2.8x |
This table can be generated with ./ci.sh benchmark -a -html
.
Modules
go-toml uses Go's standard modules system.
Installation instructions:
- Go ⥠1.16: Nothing to do. Use the import in your code. The
go
command deals with it automatically. - Go ⥠1.13:
GO111MODULE=on go get github.com/pelletier/go-toml/v2
.
In case of trouble: Go Modules FAQ.
Tools
Go-toml provides three handy command line tools:
-
tomljson
: Reads a TOML file and outputs its JSON representation.$ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest $ tomljson --help
-
jsontoml
: Reads a JSON file and outputs a TOML representation.$ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest $ jsontoml --help
-
tomll
: Lints and reformats a TOML file.$ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest $ tomll --help
Docker image
Those tools are also available as a Docker image. For example, to use
tomljson
:
docker run -i ghcr.io/pelletier/go-toml:v2 tomljson < example.toml
Multiple versions are available on ghcr.io.
Migrating from v1
This section describes the differences between v1 and v2, with some pointers on how to get the original behavior when possible.
Decoding / Unmarshal
Automatic field name guessing
When unmarshaling to a struct, if a key in the TOML document does not exactly
match the name of a struct field or any of the toml
-tagged field, v1 tries
multiple variations of the key (code).
V2 instead does a case-insensitive matching, like encoding/json
.
This could impact you if you are relying on casing to differentiate two fields,
and one of them is a not using the toml
struct tag. The recommended solution
is to be specific about tag names for those fields using the toml
struct tag.
Ignore preexisting value in interface
When decoding into a non-nil interface{}
, go-toml v1 uses the type of the
element in the interface to decode the object. For example:
type inner struct {
B interface{}
}
type doc struct {
A interface{}
}
d := doc{
A: inner{
B: "Before",
},
}
data := `
[A]
B = "After"
`
toml.Unmarshal([]byte(data), &d)
fmt.Printf("toml v1: %#v\n", d)
// toml v1: main.doc{A:main.inner{B:"After"}}
In this case, field A
is of type interface{}
, containing a inner
struct.
V1 sees that type and uses it when decoding the object.
When decoding an object into an interface{}
, V2 instead disregards whatever
value the interface{}
may contain and replaces it with a
map[string]interface{}
. With the same data structure as above, here is what
the result looks like:
toml.Unmarshal([]byte(data), &d)
fmt.Printf("toml v2: %#v\n", d)
// toml v2: main.doc{A:map[string]interface {}{"B":"After"}}
This is to match encoding/json
's behavior. There is no way to make the v2
decoder behave like v1.
Values out of array bounds ignored
When decoding into an array, v1 returns an error when the number of elements contained in the doc is superior to the capacity of the array. For example:
type doc struct {
A [2]string
}
d := doc{}
err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d)
fmt.Println(err)
// (1, 1): unmarshal: TOML array length (3) exceeds destination array length (2)
In the same situation, v2 ignores the last value:
err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d)
fmt.Println("err:", err, "d:", d)
// err: <nil> d: {[one two]}
This is to match encoding/json
's behavior. There is no way to make the v2
decoder behave like v1.
Support for toml.Unmarshaler
has been dropped
This method was not widely used, poorly defined, and added a lot of complexity.
A similar effect can be achieved by implementing the encoding.TextUnmarshaler
interface and use strings.
Support for default
struct tag has been dropped
This feature adds complexity and a poorly defined API for an effect that can be accomplished outside of the library.
It does not seem like other format parsers in Go support that feature (the project referenced in the original ticket #202 has not been updated since 2017). Given that go-toml v2 should not touch values not in the document, the same effect can be achieved by pre-filling the struct with defaults (libraries like go-defaults can help). Also, string representation is not well defined for all types: it creates issues like #278.
The recommended replacement is pre-filling the struct before unmarshaling.
toml.Tree
replacement
This structure was the initial attempt at providing a document model for
go-toml. It allows manipulating the structure of any document, encoding and
decoding from their TOML representation. While a more robust feature was
initially planned in go-toml v2, this has been ultimately removed from
scope of this library, with no plan to add it back at the moment. The
closest equivalent at the moment would be to unmarshal into an interface{}
and
use type assertions and/or reflection to manipulate the arbitrary
structure. However this would fall short of providing all of the TOML features
such as adding comments and be specific about whitespace.
toml.Position
are not retrievable anymore
The API for retrieving the position (line, column) of a specific TOML element do not exist anymore. This was done to minimize the amount of concepts introduced by the library (query path), and avoid the performance hit related to storing positions in the absence of a document model, for a feature that seemed to have little use. Errors however have gained more detailed position information. Position retrieval seems better fitted for a document model, which has been removed from the scope of go-toml v2 at the moment.
Encoding / Marshal
Default struct fields order
V1 emits struct fields order alphabetically by default. V2 struct fields are emitted in order they are defined. For example:
type S struct {
B string
A string
}
data := S{
B: "B",
A: "A",
}
b, _ := tomlv1.Marshal(data)
fmt.Println("v1:\n" + string(b))
b, _ = tomlv2.Marshal(data)
fmt.Println("v2:\n" + string(b))
// Output:
// v1:
// A = "A"
// B = "B"
// v2:
// B = 'B'
// A = 'A'
There is no way to make v2 encoder behave like v1. A workaround could be to
manually sort the fields alphabetically in the struct definition, or generate
struct types using reflect.StructOf
.
No indentation by default
V1 automatically indents content of tables by default. V2 does not. However the
same behavior can be obtained using Encoder.SetIndentTables
. For example:
data := map[string]interface{}{
"table": map[string]string{
"key": "value",
},
}
b, _ := tomlv1.Marshal(data)
fmt.Println("v1:\n" + string(b))
b, _ = tomlv2.Marshal(data)
fmt.Println("v2:\n" + string(b))
buf := bytes.Buffer{}
enc := tomlv2.NewEncoder(&buf)
enc.SetIndentTables(true)
enc.Encode(data)
fmt.Println("v2 Encoder:\n" + string(buf.Bytes()))
// Output:
// v1:
//
// [table]
// key = "value"
//
// v2:
// [table]
// key = 'value'
//
//
// v2 Encoder:
// [table]
// key = 'value'
Keys and strings are single quoted
V1 always uses double quotes ("
) around strings and keys that cannot be
represented bare (unquoted). V2 uses single quotes instead by default ('
),
unless a character cannot be represented, then falls back to double quotes. As a
result of this change, Encoder.QuoteMapKeys
has been removed, as it is not
useful anymore.
There is no way to make v2 encoder behave like v1.
TextMarshaler
emits as a string, not TOML
Types that implement encoding.TextMarshaler
can emit arbitrary TOML in
v1. The encoder would append the result to the output directly. In v2 the result
is wrapped in a string. As a result, this interface cannot be implemented by the
root object.
There is no way to make v2 encoder behave like v1.
Encoder.CompactComments
has been removed
Emitting compact comments is now the default behavior of go-toml. This option is not necessary anymore.
Struct tags have been merged
V1 used to provide multiple struct tags: comment
, commented
, multiline
,
toml
, and omitempty
. To behave more like the standard library, v2 has merged
toml
, multiline
, commented
, and omitempty
. For example:
type doc struct {
// v1
F string `toml:"field" multiline:"true" omitempty:"true" commented:"true"`
// v2
F string `toml:"field,multiline,omitempty,commented"`
}
Has a result, the Encoder.SetTag*
methods have been removed, as there is just
one tag now.
Encoder.ArraysWithOneElementPerLine
has been renamed
The new name is Encoder.SetArraysMultiline
. The behavior should be the same.
Encoder.Indentation
has been renamed
The new name is Encoder.SetIndentSymbol
. The behavior should be the same.
Embedded structs behave like stdlib
V1 defaults to merging embedded struct fields into the embedding struct. This
behavior was unexpected because it does not follow the standard library. To
avoid breaking backward compatibility, the Encoder.PromoteAnonymous
method was
added to make the encoder behave correctly. Given backward compatibility is not
a problem anymore, v2 does the right thing by default: it follows the behavior
of encoding/json
. Encoder.PromoteAnonymous
has been removed.
query
go-toml v1 provided the go-toml/query
package. It allowed to run
JSONPath-style queries on TOML files. This feature is not available in v2. For a
replacement, check out dasel.
This package has been removed because it was essentially not supported anymore (last commit May 2020), increased the complexity of the code base, and more complete solutions exist out there.
Versioning
Expect for parts explicitly marked otherwise, go-toml follows Semantic Versioning. The supported version of TOML is indicated at the beginning of this document. The last two major versions of Go are supported (see Go Release Policy).
License
The MIT License (MIT). Read LICENSE.
Top Related Projects
TOML parser for Golang with reflection.
Go configuration with fangs
YAML support for the Go language.
A Go port of Ruby's dotenv library (Loads environment variables from .env files)
Golang library for managing configuration data from environment variables
Mergo: merging Go structs and maps since 2013
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot