package toml
import "github.com/BurntSushi/toml"
Package toml provides facilities for decoding and encoding TOML configuration files via reflection. There is also support for delaying decoding with the Primitive type, and querying the set of keys in a TOML document with the MetaData type.
The specification implemented: https://github.com/toml-lang/toml
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify whether a file is a valid TOML document. It can also be used to print the type of each key in a TOML document.
Testing
There are two important types of tests used for this package. The first is contained inside '*_test.go' files and uses the standard Go unit testing framework. These tests are primarily devoted to holistically testing the decoder and encoder.
The second type of testing is used to verify the implementation's adherence to the TOML specification. These tests have been factored into their own project: https://github.com/BurntSushi/toml-test
The reason the tests are in a separate project is so that they can be used by
any implementation of TOML. Namely, it is language agnostic.
Example StrictDecoding shows how to detect whether there are keys in the
TOML document that weren't decoded into the value given. This is useful
for returning an error to the user if they've included extraneous fields
in their configuration.
Code:
Output: Example UnmarshalTOML shows how to implement a struct type that knows how to
unmarshal itself. The struct must take full responsibility for mapping the
values passed into the struct. The method may be used with interfaces in a
struct in cases where the actual type is not known until the data is
examined.
Code:
Output: Example Unmarshaler shows how to decode TOML strings into your own
custom data type.
Code:
Output:Example (StrictDecoding)¶
{
var blob = `
key1 = "value1"
key2 = "value2"
key3 = "value3"
`
type config struct {
Key1 string
Key3 string
}
var conf config
md, err := Decode(blob, &conf)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Undecoded keys: %q\n", md.Undecoded())
// Output:
// Undecoded keys: ["key2"]
}
Undecoded keys: ["key2"]
Example (UnmarshalTOML)¶
{
var blob = `
[[parts]]
type = "valve"
id = "valve-1"
size = 1.2
rating = 4
[[parts]]
type = "valve"
id = "valve-2"
size = 2.1
rating = 5
[[parts]]
type = "pipe"
id = "pipe-1"
length = 2.1
diameter = 12
[[parts]]
type = "cable"
id = "cable-1"
length = 12
rating = 3.1
`
o := &order{}
err := Unmarshal([]byte(blob), o)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(o.parts))
for _, part := range o.parts {
fmt.Println(part.Name())
}
// Code to implement UmarshalJSON.
// type order struct {
// // NOTE `order.parts` is a private slice of type `part` which is an
// // interface and may only be loaded from toml using the
// // UnmarshalTOML() method of the Umarshaler interface.
// parts parts
// }
// func (o *order) UnmarshalTOML(data interface{}) error {
// // NOTE the example below contains detailed type casting to show how
// // the 'data' is retrieved. In operational use, a type cast wrapper
// // may be preferred e.g.
// //
// // func AsMap(v interface{}) (map[string]interface{}, error) {
// // return v.(map[string]interface{})
// // }
// //
// // resulting in:
// // d, _ := AsMap(data)
// //
// d, _ := data.(map[string]interface{})
// parts, _ := d["parts"].([]map[string]interface{})
// for _, p := range parts {
// typ, _ := p["type"].(string)
// id, _ := p["id"].(string)
// // detect the type of part and handle each case
// switch p["type"] {
// case "valve":
// size := float32(p["size"].(float64))
// rating := int(p["rating"].(int64))
// valve := &valve{
// Type: typ,
// ID: id,
// Size: size,
// Rating: rating,
// }
// o.parts = append(o.parts, valve)
// case "pipe":
// length := float32(p["length"].(float64))
// diameter := int(p["diameter"].(int64))
// pipe := &pipe{
// Type: typ,
// ID: id,
// Length: length,
// Diameter: diameter,
// }
// o.parts = append(o.parts, pipe)
// case "cable":
// length := int(p["length"].(int64))
// rating := float32(p["rating"].(float64))
// cable := &cable{
// Type: typ,
// ID: id,
// Length: length,
// Rating: rating,
// }
// o.parts = append(o.parts, cable)
// }
// }
// return nil
// }
// type parts []part
// type part interface {
// Name() string
// }
// type valve struct {
// Type string
// ID string
// Size float32
// Rating int
// }
// func (v *valve) Name() string {
// return fmt.Sprintf("VALVE: %s", v.ID)
// }
// type pipe struct {
// Type string
// ID string
// Length float32
// Diameter int
// }
// func (p *pipe) Name() string {
// return fmt.Sprintf("PIPE: %s", p.ID)
// }
// type cable struct {
// Type string
// ID string
// Length int
// Rating float32
// }
// func (c *cable) Name() string {
// return fmt.Sprintf("CABLE: %s", c.ID)
// }
// Output:
// 4
// VALVE: valve-1
// VALVE: valve-2
// PIPE: pipe-1
// CABLE: cable-1
}
4
VALVE: valve-1
VALVE: valve-2
PIPE: pipe-1
CABLE: cable-1
Example (Unmarshaler)¶
{
blob := `
[[song]]
name = "Thunder Road"
duration = "4m49s"
[[song]]
name = "Stairway to Heaven"
duration = "8m03s"
`
type song struct {
Name string
Duration duration
}
type songs struct {
Song []song
}
var favorites songs
if _, err := Decode(blob, &favorites); err != nil {
log.Fatal(err)
}
// Code to implement the TextUnmarshaler interface for `duration`:
//
// type duration struct {
// time.Duration
// }
//
// func (d *duration) UnmarshalText(text []byte) error {
// var err error
// d.Duration, err = time.ParseDuration(string(text))
// return err
// }
for _, s := range favorites.Song {
fmt.Printf("%s (%s)\n", s.Name, s.Duration)
}
// Output:
// Thunder Road (4m49s)
// Stairway to Heaven (8m3s)
}
Thunder Road (4m49s)
Stairway to Heaven (8m3s)
Index ¶
- func PrimitiveDecode(primValue Primitive, v interface{}) error
- func Unmarshal(p []byte, v interface{}) error
- type Encoder
- type Key
- type MetaData
- func Decode(data string, v interface{}) (MetaData, error)
- func DecodeFile(fpath string, v interface{}) (MetaData, error)
- func DecodeReader(r io.Reader, v interface{}) (MetaData, error)
- func (md *MetaData) IsDefined(key ...string) bool
- func (md *MetaData) Keys() []Key
- func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error
- func (md *MetaData) Type(key ...string) string
- func (md *MetaData) Undecoded() []Key
- type Primitive
- type TextMarshaler
- type TextUnmarshaler
- type Unmarshaler
- Bugs
Examples ¶
- package (StrictDecoding)
- package (UnmarshalTOML)
- package (Unmarshaler)
- Decode
- Encoder.Encode
- MetaData.PrimitiveDecode
Functions ¶
func PrimitiveDecode ¶
DEPRECATED!
Use MetaData.PrimitiveDecode instead.
func Unmarshal ¶
Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
Types ¶
type Encoder ¶
type Encoder struct { // A single indentation level. By default it is two spaces. Indent string // contains filtered or unexported fields }
Encoder controls the encoding of Go values to a TOML document to some io.Writer.
The indentation level can be controlled with the Indent field.
func NewEncoder ¶
NewEncoder returns a TOML encoder that encodes Go values to the io.Writer given. By default, a single indentation level is 2 spaces.
func (*Encoder) Encode ¶
Encode writes a TOML representation of the Go value to the underlying io.Writer. If the value given cannot be encoded to a valid TOML document, then an error is returned.
The mapping between Go values and TOML values should be precisely the same as for the Decode* functions. Similarly, the TextMarshaler interface is supported by encoding the resulting bytes as strings. (If you want to write arbitrary binary data then you will need to use something like base64 since TOML does not have any binary types.)
When encoding TOML hashes (i.e., Go maps or structs), keys without any sub-hashes are encoded first.
If a Go map is encoded, then its keys are sorted alphabetically for deterministic output. More control over this behavior may be provided if there is demand for it.
Encoding Go values without a corresponding TOML representation---like map
types with non-string keys---will cause an error to be returned. Similarly
for mixed arrays/slices, arrays/slices with nil elements, embedded
non-struct types and nested slices containing maps or structs.
(e.g., [][]map[string]string is not allowed but []map[string]string is OK
and so is []map[string][]string.)
Code:
Output:Example¶
{
date, _ := time.Parse(time.RFC822, "14 Mar 10 18:00 UTC")
var config = map[string]interface{}{
"date": date,
"counts": []int{1, 1, 2, 3, 5, 8},
"hash": map[string]string{
"key1": "val1",
"key2": "val2",
},
}
buf := new(bytes.Buffer)
if err := NewEncoder(buf).Encode(config); err != nil {
log.Fatal(err)
}
fmt.Println(buf.String())
// Output:
// counts = [1, 1, 2, 3, 5, 8]
// date = 2010-03-14T18:00:00Z
//
// [hash]
// key1 = "val1"
// key2 = "val2"
}
counts = [1, 1, 2, 3, 5, 8]
date = 2010-03-14T18:00:00Z
[hash]
key1 = "val1"
key2 = "val2"
type Key ¶
type Key []string
Key is the type of any TOML key, including key groups. Use (MetaData).Keys to get values of this type.
func (Key) String ¶
type MetaData ¶
type MetaData struct {
// contains filtered or unexported fields
}
MetaData allows access to meta information about TOML data that may not be inferrable via reflection. In particular, whether a key has been defined and the TOML type of a key.
func Decode ¶
Decode will decode the contents of `data` in TOML format into a pointer `v`.
TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be used interchangeably.)
TOML arrays of tables correspond to either a slice of structs or a slice of maps.
TOML datetimes correspond to Go `time.Time` values.
All other TOML types (float, string, int, bool and array) correspond to the obvious Go types.
An exception to the above rules is if a type implements the encoding.TextUnmarshaler interface. In this case, any primitive TOML value (floats, strings, integers, booleans and datetimes) will be converted to a byte string and given to the value's UnmarshalText method. See the Unmarshaler example for a demonstration with time duration strings.
Key mapping
TOML keys can map to either keys in a Go map or field names in a Go struct. The special `toml` struct tag may be used to map TOML keys to struct fields that don't match the key name exactly. (See the example.) A case insensitive match to struct names will be tried if an exact match can't be found.
The mapping between TOML values and Go values is loose. That is, there may exist TOML values that cannot be placed into your representation, and there may be parts of your representation that do not correspond to TOML values. This loose mapping can be made stricter by using the IsDefined and/or Undecoded methods on the MetaData returned.
This decoder will not handle cyclic types. If a cyclic type is passed,
`Decode` will not terminate.
Code:
Output:Example¶
{
var tomlBlob = `
# Some comments.
[alpha]
ip = "10.0.0.1"
[alpha.config]
Ports = [ 8001, 8002 ]
Location = "Toronto"
Created = 1987-07-05T05:45:00Z
[beta]
ip = "10.0.0.2"
[beta.config]
Ports = [ 9001, 9002 ]
Location = "New Jersey"
Created = 1887-01-05T05:55:00Z
`
type serverConfig struct {
Ports []int
Location string
Created time.Time
}
type server struct {
IP string `toml:"ip,omitempty"`
Config serverConfig `toml:"config"`
}
type servers map[string]server
var config servers
if _, err := Decode(tomlBlob, &config); err != nil {
log.Fatal(err)
}
for _, name := range []string{"alpha", "beta"} {
s := config[name]
fmt.Printf("Server: %s (ip: %s) in %s created on %s\n",
name, s.IP, s.Config.Location,
s.Config.Created.Format("2006-01-02"))
fmt.Printf("Ports: %v\n", s.Config.Ports)
}
// Output:
// Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
// Ports: [8001 8002]
// Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
// Ports: [9001 9002]
}
Server: alpha (ip: 10.0.0.1) in Toronto created on 1987-07-05
Ports: [8001 8002]
Server: beta (ip: 10.0.0.2) in New Jersey created on 1887-01-05
Ports: [9001 9002]
func DecodeFile ¶
DecodeFile is just like Decode, except it will automatically read the contents of the file at `fpath` and decode it for you.
func DecodeReader ¶
DecodeReader is just like Decode, except it will consume all bytes from the reader and decode it for you.
func (*MetaData) IsDefined ¶
IsDefined returns true if the key given exists in the TOML data. The key should be specified hierarchially. e.g.,
// access the TOML key 'a.b.c' IsDefined("a", "b", "c")
IsDefined will return false if an empty key given. Keys are case sensitive.
func (*MetaData) Keys ¶
Keys returns a slice of every key in the TOML data, including key groups. Each key is itself a slice, where the first element is the top of the hierarchy and the last is the most specific.
The list will have the same order as the keys appeared in the TOML data.
All keys returned are non-empty.
func (*MetaData) PrimitiveDecode ¶
PrimitiveDecode is just like the other `Decode*` functions, except it decodes a TOML value that has already been parsed. Valid primitive values can *only* be obtained from values filled by the decoder functions, including this method. (i.e., `v` may contain more `Primitive` values.)
Meta data for primitive values is included in the meta data returned by
the `Decode*` functions with one exception: keys returned by the Undecoded
method will only reflect keys that were decoded. Namely, any keys hidden
behind a Primitive will be considered undecoded. Executing this method will
update the undecoded keys in the meta data. (See the example.)
Code:
Output:Example¶
{
var md MetaData
var err error
var tomlBlob = `
ranking = ["Springsteen", "J Geils"]
[bands.Springsteen]
started = 1973
albums = ["Greetings", "WIESS", "Born to Run", "Darkness"]
[bands."J Geils"]
started = 1970
albums = ["The J. Geils Band", "Full House", "Blow Your Face Out"]
`
type band struct {
Started int
Albums []string
}
type classics struct {
Ranking []string
Bands map[string]Primitive
}
// Do the initial decode. Reflection is delayed on Primitive values.
var music classics
if md, err = Decode(tomlBlob, &music); err != nil {
log.Fatal(err)
}
// MetaData still includes information on Primitive values.
fmt.Printf("Is `bands.Springsteen` defined? %v\n",
md.IsDefined("bands", "Springsteen"))
// Decode primitive data into Go values.
for _, artist := range music.Ranking {
// A band is a primitive value, so we need to decode it to get a
// real `band` value.
primValue := music.Bands[artist]
var aBand band
if err = md.PrimitiveDecode(primValue, &aBand); err != nil {
log.Fatal(err)
}
fmt.Printf("%s started in %d.\n", artist, aBand.Started)
}
// Check to see if there were any fields left undecoded.
// Note that this won't be empty before decoding the Primitive value!
fmt.Printf("Undecoded: %q\n", md.Undecoded())
// Output:
// Is `bands.Springsteen` defined? true
// Springsteen started in 1973.
// J Geils started in 1970.
// Undecoded: []
}
Is `bands.Springsteen` defined? true
Springsteen started in 1973.
J Geils started in 1970.
Undecoded: []
func (*MetaData) Type ¶
Type returns a string representation of the type of the key specified.
Type will return the empty string if given an empty key or a key that does not exist. Keys are case sensitive.
func (*MetaData) Undecoded ¶
Undecoded returns all keys that have not been decoded in the order in which they appear in the original TOML document.
This includes keys that haven't been decoded because of a Primitive value. Once the Primitive value is decoded, the keys will be considered decoded.
Also note that decoding into an empty interface will result in no decoding, and so no keys will be considered decoded.
In this sense, the Undecoded keys correspond to keys in the TOML document that do not have a concrete type in your representation.
type Primitive ¶
type Primitive struct {
// contains filtered or unexported fields
}
Primitive is a TOML value that hasn't been decoded into a Go value. When using the various `Decode*` functions, the type `Primitive` may be given to any value, and its decoding will be delayed.
A `Primitive` value can be decoded using the `PrimitiveDecode` function.
The underlying representation of a `Primitive` value is subject to change. Do not rely on it.
N.B. Primitive values are still parsed, so using them will only avoid the overhead of reflection. They can be useful when you don't know the exact type of TOML data until run time.
type TextMarshaler ¶
type TextMarshaler encoding.TextMarshaler
TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here so that Go 1.1 can be supported.
type TextUnmarshaler ¶
type TextUnmarshaler encoding.TextUnmarshaler
TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined here so that Go 1.1 can be supported.
type Unmarshaler ¶
type Unmarshaler interface { UnmarshalTOML(interface{}) error }
Unmarshaler is the interface implemented by objects that can unmarshal a TOML description of themselves.
Bugs ¶
☞ The behavior here is incorrect whenever a Go type satisfies the encoding.TextUnmarshaler interface but also corresponds to a TOML hash or array. In particular, the unmarshaler should only be applied to primitive TOML values. But at this point, it will be applied to all kinds of values and produce an incorrect error whenever those values are hashes or arrays (including arrays of tables).
Source Files ¶
decode.go decode_meta.go doc.go encode.go encoding_types.go lex.go parse.go type_check.go type_fields.go
Directories ¶
Path | Synopsis |
---|---|
cmd | |
cmd/toml-test-decoder | Command toml-test-decoder satisfies the toml-test interface for testing TOML decoders. |
cmd/toml-test-encoder | Command toml-test-encoder satisfies the toml-test interface for testing TOML encoders. |
cmd/tomlv | Command tomlv validates TOML documents and prints each key's type. |
_examples |
- Version
- v0.3.0
- Published
- Mar 28, 2017
- Platform
- linux/amd64
- Imports
- 15 packages
- Last checked
- 12 hours ago –
Tools for package owners.