...

Text file src/github.com/pelletier/go-toml/v2/README.md

Documentation: github.com/pelletier/go-toml/v2

     1# go-toml v2
     2
     3Go library for the [TOML](https://toml.io/en/) format.
     4
     5This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0).
     6
     7[🐞 Bug Reports](https://github.com/pelletier/go-toml/issues)
     8
     9[💬 Anything else](https://github.com/pelletier/go-toml/discussions)
    10
    11## Documentation
    12
    13Full API, examples, and implementation notes are available in the Go
    14documentation.
    15
    16[![Go Reference](https://pkg.go.dev/badge/github.com/pelletier/go-toml/v2.svg)](https://pkg.go.dev/github.com/pelletier/go-toml/v2)
    17
    18## Import
    19
    20```go
    21import "github.com/pelletier/go-toml/v2"
    22```
    23
    24See [Modules](#Modules).
    25
    26## Features
    27
    28### Stdlib behavior
    29
    30As much as possible, this library is designed to behave similarly as the
    31standard library's `encoding/json`.
    32
    33### Performance
    34
    35While go-toml favors usability, it is written with performance in mind. Most
    36operations should not be shockingly slow. See [benchmarks](#benchmarks).
    37
    38### Strict mode
    39
    40`Decoder` can be set to "strict mode", which makes it error when some parts of
    41the TOML document was not present in the target structure. This is a great way
    42to check for typos. [See example in the documentation][strict].
    43
    44[strict]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Decoder.DisallowUnknownFields
    45
    46### Contextualized errors
    47
    48When most decoding errors occur, go-toml returns [`DecodeError`][decode-err],
    49which contains a human readable contextualized version of the error. For
    50example:
    51
    52```
    531| [server]
    542| path = 100
    55 |        ~~~ cannot decode TOML integer into struct field toml_test.Server.Path of type string
    563| port = 50
    57```
    58
    59[decode-err]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#DecodeError
    60
    61### Local date and time support
    62
    63TOML supports native [local date/times][ldt]. It allows to represent a given
    64date, time, or date-time without relation to a timezone or offset. To support
    65this use-case, go-toml provides [`LocalDate`][tld], [`LocalTime`][tlt], and
    66[`LocalDateTime`][tldt]. Those types can be transformed to and from `time.Time`,
    67making them convenient yet unambiguous structures for their respective TOML
    68representation.
    69
    70[ldt]: https://toml.io/en/v1.0.0#local-date-time
    71[tld]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDate
    72[tlt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalTime
    73[tldt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDateTime
    74
    75### Commented config
    76
    77Since TOML is often used for configuration files, go-toml can emit documents
    78annotated with [comments and commented-out values][comments-example]. For
    79example, it can generate the following file:
    80
    81```toml
    82# Host IP to connect to.
    83host = '127.0.0.1'
    84# Port of the remote server.
    85port = 4242
    86
    87# Encryption parameters (optional)
    88# [TLS]
    89# cipher = 'AEAD-AES128-GCM-SHA256'
    90# version = 'TLS 1.3'
    91```
    92
    93[comments-example]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Marshal-Commented
    94
    95## Getting started
    96
    97Given the following struct, let's see how to read it and write it as TOML:
    98
    99```go
   100type MyConfig struct {
   101      Version int
   102      Name    string
   103      Tags    []string
   104}
   105```
   106
   107### Unmarshaling
   108
   109[`Unmarshal`][unmarshal] reads a TOML document and fills a Go structure with its
   110content. For example:
   111
   112```go
   113doc := `
   114version = 2
   115name = "go-toml"
   116tags = ["go", "toml"]
   117`
   118
   119var cfg MyConfig
   120err := toml.Unmarshal([]byte(doc), &cfg)
   121if err != nil {
   122      panic(err)
   123}
   124fmt.Println("version:", cfg.Version)
   125fmt.Println("name:", cfg.Name)
   126fmt.Println("tags:", cfg.Tags)
   127
   128// Output:
   129// version: 2
   130// name: go-toml
   131// tags: [go toml]
   132```
   133
   134[unmarshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Unmarshal
   135
   136### Marshaling
   137
   138[`Marshal`][marshal] is the opposite of Unmarshal: it represents a Go structure
   139as a TOML document:
   140
   141```go
   142cfg := MyConfig{
   143      Version: 2,
   144      Name:    "go-toml",
   145      Tags:    []string{"go", "toml"},
   146}
   147
   148b, err := toml.Marshal(cfg)
   149if err != nil {
   150      panic(err)
   151}
   152fmt.Println(string(b))
   153
   154// Output:
   155// Version = 2
   156// Name = 'go-toml'
   157// Tags = ['go', 'toml']
   158```
   159
   160[marshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Marshal
   161
   162## Unstable API
   163
   164This API does not yet follow the backward compatibility guarantees of this
   165library. They provide early access to features that may have rough edges or an
   166API subject to change.
   167
   168### Parser
   169
   170Parser is the unstable API that allows iterative parsing of a TOML document at
   171the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable.
   172
   173## Benchmarks
   174
   175Execution time speedup compared to other Go TOML libraries:
   176
   177<table>
   178    <thead>
   179        <tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
   180    </thead>
   181    <tbody>
   182        <tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>2.2x</td></tr>
   183        <tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>2.1x</td></tr>
   184        <tr><td>Marshal/ReferenceFile/struct-2</td><td>2.2x</td><td>3.0x</td></tr>
   185        <tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.7x</td></tr>
   186        <tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.6x</td><td>2.7x</td></tr>
   187        <tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.6x</td><td>5.1x</td></tr>
   188     </tbody>
   189</table>
   190<details><summary>See more</summary>
   191<p>The table above has the results of the most common use-cases. The table below
   192contains the results of all benchmarks, including unrealistic ones. It is
   193provided for completeness.</p>
   194
   195<table>
   196    <thead>
   197        <tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
   198    </thead>
   199    <tbody>
   200        <tr><td>Marshal/SimpleDocument/map-2</td><td>1.8x</td><td>2.7x</td></tr>
   201        <tr><td>Marshal/SimpleDocument/struct-2</td><td>2.7x</td><td>3.8x</td></tr>
   202        <tr><td>Unmarshal/SimpleDocument/map-2</td><td>3.8x</td><td>3.0x</td></tr>
   203        <tr><td>Unmarshal/SimpleDocument/struct-2</td><td>5.6x</td><td>4.1x</td></tr>
   204        <tr><td>UnmarshalDataset/example-2</td><td>3.0x</td><td>3.2x</td></tr>
   205        <tr><td>UnmarshalDataset/code-2</td><td>2.3x</td><td>2.9x</td></tr>
   206        <tr><td>UnmarshalDataset/twitter-2</td><td>2.6x</td><td>2.7x</td></tr>
   207        <tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.2x</td><td>2.3x</td></tr>
   208        <tr><td>UnmarshalDataset/canada-2</td><td>1.8x</td><td>1.5x</td></tr>
   209        <tr><td>UnmarshalDataset/config-2</td><td>4.1x</td><td>2.9x</td></tr>
   210        <tr><td>geomean</td><td>2.7x</td><td>2.8x</td></tr>
   211     </tbody>
   212</table>
   213<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>
   214</details>
   215
   216## Modules
   217
   218go-toml uses Go's standard modules system.
   219
   220Installation instructions:
   221
   222- Go ≥ 1.16: Nothing to do. Use the import in your code. The `go` command deals
   223  with it automatically.
   224- Go ≥ 1.13: `GO111MODULE=on go get github.com/pelletier/go-toml/v2`.
   225
   226In case of trouble: [Go Modules FAQ][mod-faq].
   227
   228[mod-faq]: https://github.com/golang/go/wiki/Modules#why-does-installing-a-tool-via-go-get-fail-with-error-cannot-find-main-module
   229
   230## Tools
   231
   232Go-toml provides three handy command line tools:
   233
   234 * `tomljson`: Reads a TOML file and outputs its JSON representation.
   235
   236    ```
   237    $ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest
   238    $ tomljson --help
   239    ```
   240
   241 * `jsontoml`: Reads a JSON file and outputs a TOML representation.
   242
   243    ```
   244    $ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest
   245    $ jsontoml --help
   246    ```
   247
   248 * `tomll`: Lints and reformats a TOML file.
   249
   250    ```
   251    $ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest
   252    $ tomll --help
   253    ```
   254
   255### Docker image
   256
   257Those tools are also available as a [Docker image][docker]. For example, to use
   258`tomljson`:
   259
   260```
   261docker run -i ghcr.io/pelletier/go-toml:v2 tomljson < example.toml
   262```
   263
   264Multiple versions are availble on [ghcr.io][docker].
   265
   266[docker]: https://github.com/pelletier/go-toml/pkgs/container/go-toml
   267
   268## Migrating from v1
   269
   270This section describes the differences between v1 and v2, with some pointers on
   271how to get the original behavior when possible.
   272
   273### Decoding / Unmarshal
   274
   275#### Automatic field name guessing
   276
   277When unmarshaling to a struct, if a key in the TOML document does not exactly
   278match the name of a struct field or any of the `toml`-tagged field, v1 tries
   279multiple variations of the key ([code][v1-keys]).
   280
   281V2 instead does a case-insensitive matching, like `encoding/json`.
   282
   283This could impact you if you are relying on casing to differentiate two fields,
   284and one of them is a not using the `toml` struct tag. The recommended solution
   285is to be specific about tag names for those fields using the `toml` struct tag.
   286
   287[v1-keys]: https://github.com/pelletier/go-toml/blob/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal.go#L775-L781
   288
   289#### Ignore preexisting value in interface
   290
   291When decoding into a non-nil `interface{}`, go-toml v1 uses the type of the
   292element in the interface to decode the object. For example:
   293
   294```go
   295type inner struct {
   296  B interface{}
   297}
   298type doc struct {
   299  A interface{}
   300}
   301
   302d := doc{
   303  A: inner{
   304    B: "Before",
   305  },
   306}
   307
   308data := `
   309[A]
   310B = "After"
   311`
   312
   313toml.Unmarshal([]byte(data), &d)
   314fmt.Printf("toml v1: %#v\n", d)
   315
   316// toml v1: main.doc{A:main.inner{B:"After"}}
   317```
   318
   319In this case, field `A` is of type `interface{}`, containing a `inner` struct.
   320V1 sees that type and uses it when decoding the object.
   321
   322When decoding an object into an `interface{}`, V2 instead disregards whatever
   323value the `interface{}` may contain and replaces it with a
   324`map[string]interface{}`. With the same data structure as above, here is what
   325the result looks like:
   326
   327```go
   328toml.Unmarshal([]byte(data), &d)
   329fmt.Printf("toml v2: %#v\n", d)
   330
   331// toml v2: main.doc{A:map[string]interface {}{"B":"After"}}
   332```
   333
   334This is to match `encoding/json`'s behavior. There is no way to make the v2
   335decoder behave like v1.
   336
   337#### Values out of array bounds ignored
   338
   339When decoding into an array, v1 returns an error when the number of elements
   340contained in the doc is superior to the capacity of the array. For example:
   341
   342```go
   343type doc struct {
   344  A [2]string
   345}
   346d := doc{}
   347err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d)
   348fmt.Println(err)
   349
   350// (1, 1): unmarshal: TOML array length (3) exceeds destination array length (2)
   351```
   352
   353In the same situation, v2 ignores the last value:
   354
   355```go
   356err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d)
   357fmt.Println("err:", err, "d:", d)
   358// err: <nil> d: {[one two]}
   359```
   360
   361This is to match `encoding/json`'s behavior. There is no way to make the v2
   362decoder behave like v1.
   363
   364#### Support for `toml.Unmarshaler` has been dropped
   365
   366This method was not widely used, poorly defined, and added a lot of complexity.
   367A similar effect can be achieved by implementing the `encoding.TextUnmarshaler`
   368interface and use strings.
   369
   370#### Support for `default` struct tag has been dropped
   371
   372This feature adds complexity and a poorly defined API for an effect that can be
   373accomplished outside of the library.
   374
   375It does not seem like other format parsers in Go support that feature (the
   376project referenced in the original ticket #202 has not been updated since 2017).
   377Given that go-toml v2 should not touch values not in the document, the same
   378effect can be achieved by pre-filling the struct with defaults (libraries like
   379[go-defaults][go-defaults] can help). Also, string representation is not well
   380defined for all types: it creates issues like #278.
   381
   382The recommended replacement is pre-filling the struct before unmarshaling.
   383
   384[go-defaults]: https://github.com/mcuadros/go-defaults
   385
   386#### `toml.Tree` replacement
   387
   388This structure was the initial attempt at providing a document model for
   389go-toml. It allows manipulating the structure of any document, encoding and
   390decoding from their TOML representation. While a more robust feature was
   391initially planned in go-toml v2, this has been ultimately [removed from
   392scope][nodoc] of this library, with no plan to add it back at the moment. The
   393closest equivalent at the moment would be to unmarshal into an `interface{}` and
   394use type assertions and/or reflection to manipulate the arbitrary
   395structure. However this would fall short of providing all of the TOML features
   396such as adding comments and be specific about whitespace.
   397
   398
   399#### `toml.Position` are not retrievable anymore
   400
   401The API for retrieving the position (line, column) of a specific TOML element do
   402not exist anymore. This was done to minimize the amount of concepts introduced
   403by the library (query path), and avoid the performance hit related to storing
   404positions in the absence of a document model, for a feature that seemed to have
   405little use. Errors however have gained more detailed position
   406information. Position retrieval seems better fitted for a document model, which
   407has been [removed from the scope][nodoc] of go-toml v2 at the moment.
   408
   409### Encoding / Marshal
   410
   411#### Default struct fields order
   412
   413V1 emits struct fields order alphabetically by default. V2 struct fields are
   414emitted in order they are defined. For example:
   415
   416```go
   417type S struct {
   418	B string
   419	A string
   420}
   421
   422data := S{
   423	B: "B",
   424	A: "A",
   425}
   426
   427b, _ := tomlv1.Marshal(data)
   428fmt.Println("v1:\n" + string(b))
   429
   430b, _ = tomlv2.Marshal(data)
   431fmt.Println("v2:\n" + string(b))
   432
   433// Output:
   434// v1:
   435// A = "A"
   436// B = "B"
   437
   438// v2:
   439// B = 'B'
   440// A = 'A'
   441```
   442
   443There is no way to make v2 encoder behave like v1. A workaround could be to
   444manually sort the fields alphabetically in the struct definition, or generate
   445struct types using `reflect.StructOf`.
   446
   447#### No indentation by default
   448
   449V1 automatically indents content of tables by default. V2 does not. However the
   450same behavior can be obtained using [`Encoder.SetIndentTables`][sit]. For example:
   451
   452```go
   453data := map[string]interface{}{
   454	"table": map[string]string{
   455		"key": "value",
   456	},
   457}
   458
   459b, _ := tomlv1.Marshal(data)
   460fmt.Println("v1:\n" + string(b))
   461
   462b, _ = tomlv2.Marshal(data)
   463fmt.Println("v2:\n" + string(b))
   464
   465buf := bytes.Buffer{}
   466enc := tomlv2.NewEncoder(&buf)
   467enc.SetIndentTables(true)
   468enc.Encode(data)
   469fmt.Println("v2 Encoder:\n" + string(buf.Bytes()))
   470
   471// Output:
   472// v1:
   473//
   474// [table]
   475//   key = "value"
   476//
   477// v2:
   478// [table]
   479// key = 'value'
   480//
   481//
   482// v2 Encoder:
   483// [table]
   484//   key = 'value'
   485```
   486
   487[sit]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Encoder.SetIndentTables
   488
   489#### Keys and strings are single quoted
   490
   491V1 always uses double quotes (`"`) around strings and keys that cannot be
   492represented bare (unquoted). V2 uses single quotes instead by default (`'`),
   493unless a character cannot be represented, then falls back to double quotes. As a
   494result of this change, `Encoder.QuoteMapKeys` has been removed, as it is not
   495useful anymore.
   496
   497There is no way to make v2 encoder behave like v1.
   498
   499#### `TextMarshaler` emits as a string, not TOML
   500
   501Types that implement [`encoding.TextMarshaler`][tm] can emit arbitrary TOML in
   502v1. The encoder would append the result to the output directly. In v2 the result
   503is wrapped in a string. As a result, this interface cannot be implemented by the
   504root object.
   505
   506There is no way to make v2 encoder behave like v1.
   507
   508[tm]: https://golang.org/pkg/encoding/#TextMarshaler
   509
   510#### `Encoder.CompactComments` has been removed
   511
   512Emitting compact comments is now the default behavior of go-toml. This option
   513is not necessary anymore.
   514
   515#### Struct tags have been merged
   516
   517V1 used to provide multiple struct tags: `comment`, `commented`, `multiline`,
   518`toml`, and `omitempty`. To behave more like the standard library, v2 has merged
   519`toml`, `multiline`, `commented`, and `omitempty`. For example:
   520
   521```go
   522type doc struct {
   523	// v1
   524	F string `toml:"field" multiline:"true" omitempty:"true" commented:"true"`
   525	// v2
   526	F string `toml:"field,multiline,omitempty,commented"`
   527}
   528```
   529
   530Has a result, the `Encoder.SetTag*` methods have been removed, as there is just
   531one tag now.
   532
   533#### `Encoder.ArraysWithOneElementPerLine` has been renamed
   534
   535The new name is `Encoder.SetArraysMultiline`. The behavior should be the same.
   536
   537#### `Encoder.Indentation` has been renamed
   538
   539The new name is `Encoder.SetIndentSymbol`. The behavior should be the same.
   540
   541
   542#### Embedded structs behave like stdlib
   543
   544V1 defaults to merging embedded struct fields into the embedding struct. This
   545behavior was unexpected because it does not follow the standard library. To
   546avoid breaking backward compatibility, the `Encoder.PromoteAnonymous` method was
   547added to make the encoder behave correctly. Given backward compatibility is not
   548a problem anymore, v2 does the right thing by default: it follows the behavior
   549of `encoding/json`. `Encoder.PromoteAnonymous` has been removed.
   550
   551[nodoc]: https://github.com/pelletier/go-toml/discussions/506#discussioncomment-1526038
   552
   553### `query`
   554
   555go-toml v1 provided the [`go-toml/query`][query] package. It allowed to run
   556JSONPath-style queries on TOML files. This feature is not available in v2. For a
   557replacement, check out [dasel][dasel].
   558
   559This package has been removed because it was essentially not supported anymore
   560(last commit May 2020), increased the complexity of the code base, and more
   561complete solutions exist out there.
   562
   563[query]: https://github.com/pelletier/go-toml/tree/f99d6bbca119636aeafcf351ee52b3d202782627/query
   564[dasel]: https://github.com/TomWright/dasel
   565
   566## Versioning
   567
   568Go-toml follows [Semantic Versioning](https://semver.org). The supported version
   569of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of
   570this document. The last two major versions of Go are supported
   571(see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)).
   572
   573## License
   574
   575The MIT License (MIT). Read [LICENSE](LICENSE).

View as plain text