TOML parser for Golang with reflection.


The last commit to this repo before writing this message occurred over two years ago. While it was never my intention to abandon this package, it's clear that I have.

While many issues and PRs have piled up, the most significant short-coming of this library at present is that it has fallen behind the upstream TOML specification.

Many folks are still using this package, and so long as you're happy, there's no pressing reason to switch to something else. With that said, it would probably be wise to organize around an existing alternative or fork this project and update it.

I have no plans on handing ownership of this project over to someone else, unless that someone can meet an extremely high bar that inspires confidence that the project will be in good hands. In particular, I've had mostly poor (but not always) experiences with handing maintenance of a project over to someone else.

TOML parser and encoder for Go with reflection

TOML stands for Tom's Obvious, Minimal Language. This Go package provides a reflection interface similar to Go's standard library json and xml packages. This package also supports the encoding.TextUnmarshaler and encoding.TextMarshaler interfaces so that you can define custom data representations. (There is an example of this below.)


Compatible with TOML version v0.4.0



go get

Try the toml validator:

go get
tomlv some-toml-file.toml

Build Status GoDoc


This package passes all tests in toml-test for both the decoder and the encoder.


This package works similarly to how the Go standard library handles XML and JSON. Namely, data is loaded into Go values via reflection.

For the simplest example, consider some TOML file as just a list of keys and values:

Age = 25
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z

Which could be defined in Go as:

type Config struct {
  Age int
  Cats []string
  Pi float64
  Perfection []int
  DOB time.Time // requires `import time`

And then decoded with:

var conf Config
if _, err := toml.Decode(tomlData, &conf); err != nil {
  // handle error

You can also use struct tags if your struct field name doesn't map to a TOML key value directly:

some_key_NAME = "wat"
type TOML struct {
  ObscureKey string `toml:"some_key_NAME"`

Using the encoding.TextUnmarshaler interface

Here's an example that automatically parses duration strings into time.Duration values:

name = "Thunder Road"
duration = "4m49s"

name = "Stairway to Heaven"
duration = "8m03s"

Which can be decoded with:

type song struct {
  Name     string
  Duration duration
type songs struct {
  Song []song
var favorites songs
if _, err := toml.Decode(blob, &favorites); err != nil {

for _, s := range favorites.Song {
  fmt.Printf("%s (%s)\n", s.Name, s.Duration)

And you'll also need a duration type that satisfies the encoding.TextUnmarshaler interface:

type duration struct {

func (d *duration) UnmarshalText(text []byte) error {
	var err error
	d.Duration, err = time.ParseDuration(string(text))
	return err

More complex usage

Here's an example of how to load the example from the official spec page:

# This is a TOML document. Boom.

title = "TOML Example"

name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z # First class dates? Why not?

server = ""
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true


  # You can indent as you please. Tabs or spaces. TOML don't care.
  ip = ""
  dc = "eqdc10"

  ip = ""
  dc = "eqdc10"

data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it

# Line breaks are OK when inside arrays
hosts = [

And the corresponding Go types are:

type tomlConfig struct {
	Title string
	Owner ownerInfo
	DB database `toml:"database"`
	Servers map[string]server
	Clients clients

type ownerInfo struct {
	Name string
	Org string `toml:"organization"`
	Bio string
	DOB time.Time

type database struct {
	Server string
	Ports []int
	ConnMax int `toml:"connection_max"`
	Enabled bool

type server struct {
	IP string
	DC string

type clients struct {
	Data [][]interface{}
	Hosts []string

Note that a case insensitive match will be tried if an exact match can't be found.

A working example of the above can be found in _examples/example.{go,toml}.

Andrew Gallant
I love to code.
Andrew Gallant
  • Inline Table Support

    Inline Table Support

    Hey, forgive me if I missed this. Recently, inline tables made it into the spec, is this on the roadmap for being supported in this package soon (meaning what kind of priority would supporting inline tables be, if it's one at all)? Just wondering! Also, awesome library!

  • Encoding: 'omitempty' will panic if the struct contains a uncomparable type

    Encoding: 'omitempty' will panic if the struct contains a uncomparable type

    While encoding a struct will be checked for emptiness with the isEmpty function:

    func isEmpty(rv reflect.Value) bool {
    	switch rv.Kind() {
    	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
    		return rv.Len() == 0
    	case reflect.Struct:
    		return reflect.Zero(rv.Type()).Interface() == rv.Interface()
    	case reflect.Bool:
    		return !rv.Bool()
    	return false

    The comparing of structs panics if the structs contains any uncomparable type.

    Minimal example:

    type a struct {
    	B b `toml:"B,omitempty"`
    type b struct {
    	C []string `toml:"C,omitempty"`
    func TestToml(t *testing.T) {
    	b1 := b{C: []string{"foo"}}
    	a1 := a{B: b1}
    	var buf bytes.Buffer
    	fmt.Printf("%s", buf.String())


    === RUN   TestToml
    --- FAIL: TestToml (0.00s)
    panic: runtime error: comparing uncomparable type data.b [recovered]
    	panic: runtime error: comparing uncomparable type data.b [recovered]
    	panic: runtime error: comparing uncomparable type data.b
  • Encoder [WIP]

    Encoder [WIP]

    Work-in-progress improvements to the encoder. I'm trying to get it to be fully functioning and would appreciate help and/or code reviews.


    • [x] Array of tables
    • [ ] Nested arrays of tables
  • Cannot decode integer value to float64 type.

    Cannot decode integer value to float64 type.

    It seems like a struct containing a float64 field cannot be decoded from a TOML document containing an integer value. The following example demonstrates this.

    package main
    import (
    func main() {
        raw := "Foo = 123"
        var s struct {
            Foo float64
        _, err := toml.Decode(raw, &s)
        if err != nil {

    The error message printed looks like this

    2014/09/09 13:42:51 Type mismatch for 'struct { Foo float64 }.Foo': Expected float but found 'int64'.

  • Using a TextUnmarshaler in a map value doesn't work

    Using a TextUnmarshaler in a map value doesn't work

    Could you explain how to apply this

    func (d *duration) UnmarshalText(text []byte) (err error) {
    	d.Duration, err = time.ParseDuration(string(text))
    	return err


    type httpclient struct {
        Timeouts map[string]string

    to be able to write in the config like this

    client = "60s"
    2XX = "50ms"
    3XX = "1000ms"
    4XX = "2000ms"
    5XX = "5000ms"


  • add Marshaler interface

    add Marshaler interface

    Hi @BurntSushi, I've noticed that there is no nice way to handle Marshal/Encode operation unless implementing encoding.TextMarshaler interface, which can't be used if struct should be also marshalable to JSON (json libraries also looks for encoding.TextMarshaler),

    so I've added Marshaler interface with following signature:

    type Marshaler interface {
        MarshalTOML() ([]byte, error)

    Also I've added various tests for this feautre.

  • Add

    Add "extended errors"

    The lexer and parser now always return a ParseError; the Error() method is mostly unchanged:

    toml: line 1 (last key "x.key"): newlines not allowed within inline tables

    This adds an ErrorWithLocation() method, which will add some context where the error occurred, similar to e.g. clang or the Rust compiler:

    toml: error: newlines not allowed within inline tables
    At line 1, column 18:
          1 | x = [{ key = 42 #

    And the ErrorWithUsage() also adds some usage guidance (not always present):

    toml: error: newlines not allowed within inline tables
    At line 1, column 16:
          1 | x = [{ key = 42
    Error help:
    	Inline tables must always be on a single line:
    	    table = {key = 42, second = 43}
    	It is invalid to split them over multiple lines like so:
    	    # INVALID
    	    table = {
    		key    = 42,
    		second = 43
    	Use regular for this:
    	    key    = 42
    	    second = 43

    The line/column information should now also always be correct, and a number of error message have been tweaked a bit.

    Fixes #201 Fixes #217

  • Support for delayed parsing or unification of sections

    Support for delayed parsing or unification of sections

    I have some occasions there I might have a few sections of configuration that map to modules in a system that are not coupled with the top-level configuration loader. These sections could be considered to need somewhat dynamic unification.

    With JSON it's rather easy since one can simply put placeholders using json.RawMessage. Which allows those sections of the document to be delayed. Would you consider accepting something like a toml.RawSection or similar?

    In the meantime I've resorted to hardcoding these subsections but it's brittle and requires constant edits to ensure the compiled in packages that implement this special submodule interface are included properly in the configuration. I could also parse to a generic map and unify by hand but this results in a lot of ugly boilerplate code that has to repeated for each section type.

  • switch to MIT license

    switch to MIT license

    The WTFPL causes people to not want to use this library. Historically, I've been against changing the license mostly because it's just a pain to do so. Ideally, we'd like to get a sign off from all people that have made non-trivial contributions. (And if we can't, we either need to stop the re-licensing process or remove/rewrite those contributions. I'm not particularly inclined to the do latter, but if we can do the former, than we should.)

    So, this issue is going to track the relicensing progress. I'll start by listing some of the major contributors, where I interpreted "major" as "100 lines of code or more."

    • [x] @BurntSushi
    • [x] @cespare
    • [x] @sqs
    • [x] @rjeczalik
    • [x] @ttacon
    • [x] @bbuck
    • [x] @halostatue
    • [x] @sethwklein
  • panic runtime error while unmarshal `bad-utf8-at-end.toml` on go 1.17

    panic runtime error while unmarshal `bad-utf8-at-end.toml` on go 1.17

    Hi! thanks a lot for this useful package!

    but we got a problem with BurntSushi/toml. There is a panic while unmarshal bad-utf8-at-end.toml from your tests on go 1.17.

    it seems that runtime.panic was changes: cmd/compile,runtime: provide index information on bounds check failure. and this check doesn't work now:

    For reproduce I wrote next sample:

    	b, _ := ioutil.ReadFile("bad-utf8-at-end.toml")
    	var v interface{}
    	if err := toml.Unmarshal(b, &v); err != nil {
    		log.Fatalf("toml.Unmarshal error: %v", err)


    panic: runtime error: slice bounds out of range [238:237] [recovered]
            panic: runtime error: slice bounds out of range [238:237]
    goroutine 1 [running]:
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/parse.go:46 +0x16a
    panic({0x5072a0, 0xc000148018})
            /usr/local/go/src/runtime/panic.go:1047 +0x262*lexer).current(0xc000150000)
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/lex.go:129 +0x90*lexer).emit(0xc000150000, 0x6)
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/lex.go:133 +0x27
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/lex.go:693 +0x2f1*lexer).nextItem(0xc000150000)
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/lex.go:98 +0xf9*parser).next(0xc0001401c0)
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/parse.go:95 +0x55*parser).topLevel(0xc0001401c0, {0x12, {0xc00014c100, 0x0}, 0x5})
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/parse.go:172 +0x732{0xc00014c100, 0xf2})
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/parse.go:79 +0x4f5*Decoder).Decode(0xc000116350, {0x4f7360, 0xc000116340})
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/decode.go:120 +0x625{0xc00014c000, 0xf2}, {0x4f7360, 0xc000116340})
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/decode.go:135 +0x10f{0xc00014a000, 0xf2, 0x200}, {0x4f7360, 0xc000116340})
            /home/home/go/pkg/mod/!burnt!sushi/[email protected]/decode.go:23 +0x85
            /home/home/t/main.go:12 +0xe5

    upd: I can try to create a PR for fix it )

  • Fix override map element value during decode

    Fix override map element value during decode

    I have map, that has structures as elements.

    type LogConfig struct {
         Filename string  `toml:"file"`
         Level       string  `toml:"level"`
    type Config struct {
        Loggers map[string]*LogConfig `toml:"log"`
    fun NewConfig() *Config {
        return &Config{
             Loggers: map[string]*LogConfig {
                     "error": &LogConfig{Level: "error"},
                     "access": &LogConfig{Level: "info"},
    cfg := NewConfig()
    toml.DecodeFile("config.toml", &cfg)

    And If I do not specify the both "level and file" in config, I lose the defaults with I specify on creating Config.

    I expected that level will keep origin value if it is not specified in config.toml

  • Line ending backslash in

    Line ending backslash in """ causes later line breaks to also be removed

    Hi, I'm having trouble with breaking long lines in """ delimited strings using a backslash. This is called a "line ending backslash" in the toml spec.


    package main
    import (
    const in = `f = """
    func main() {
    	var out map[string]string
    	if _, err := toml.Decode(in, &out); err != nil {
    		fmt.Fprintf(os.Stderr, "error: %v\n", err)

    Expected output, which is generated with BurntSushi/toml v0.3.1:


    Output with BurntSushi/toml v1.2.1:


    Any chance of fixing this? Thanks!

  • omitempty incorrectly looks at values behind pointers

    omitempty incorrectly looks at values behind pointers

    isEmpty is called on a fully dereferenced struct, but if the original field type of the struct was a pointer to a struct, then omitempty should be taken to mean "drop if nil, otherwise output". In other words: isEmpty on any pointer type should return true precisely when the pointer is nil. This is currently not what happens because the field value is fully dereferenced.

    This is a leftover from #360. The issue is demonstrated by the failure in #370.

  • the unmarshal and decode API are wasteful and somewhat misleading

    the unmarshal and decode API are wasteful and somewhat misleading

    Hi, I was just looking at ways the unmarshalling and decoding is handled by this package. I must say im a bit terrifed with what I saw.

    So Unmarshalconverts bytes to string and passes it to Decode

    func Unmarshal(p []byte, v interface{}) error {
    	_, err := Decode(string(p), v)
    	return err

    Then Decodeconverts the string to io.Reader and creates a new Decoder

    func Decode(data string, v interface{}) (MetaData, error) {
    	return NewDecoder(strings.NewReader(data)).Decode(v)

    Finally, decoder.Decode, uses io.ReadAll to read everything into []bytes and converts it back into a string before calling parse.

    data, err := ioutil.ReadAll(dec.r)
    if err != nil {
    	return MetaData{}, err
    p, err := parse(string(data))
    if err != nil {
    	return MetaData{}, err

    So it seems like using the Unmarshal function is pretty wasteful since it does this coversion roudtrip.

    Additionally. it is not possible to use the decoder created by NewDecoder in a true decoder fashion, since usually it is read until EOF or another error. But EOF is never returned by decoder.Decode.

    So, the below will just hang. Although it is the expected way to use a decoder.

    for {
        if err := dec.Decode(&data); err == io.EOF {
        } else if err != nil {
  • Support encoding comments and specifying the encoding format

    Support encoding comments and specifying the encoding format

    This allows encoding comments and setting some flags to control the format. While toml.Marshaler added in #327 gives full control over how you want to format something, I don't think it's especially user-friendly to tell everyone to create a new type with all the appropriate formatting, escaping, etc. The vast majority of of use cases probably just call in to a few simple categories such as "use """ or "encode this number as a hex".

    I grouped both features together as they're closely related: both set additional information on how to write keys.

    What I want is something that:

    1. allows setting attributes programmatically;
    2. supports round-tripping by default on a standard struct;
    3. plays well with other encoders/decoders;
    4. has a reasonable uncumbersome API.

    Most options (custom types, struct tags) fail at least one of these; there were some PRs for struct tags, but they fail at 1, 2, and (arguably) 4. Custom types fail at 2, 3, and probably 4.

    This adds SetMeta() to the Encoder type; this is already what we have when decoding, and all additional information will be set on it. On MetaData we add the following types:

    SetType()       Set TOML type info.
    TypeInfo()      Get TOML type info.
    Doc()           Set "doc comment" above the key.
    Comment()       Set "inline comment" after the key.

    Every TOML type has a type in this package, which support different formatting options (see type_toml.go):


    For example:

    meta := toml.NewMetaData().
            SetType("key", toml.Int{Width: 4, Base: 16}).
            Doc("key", "A codepoint").
    Comment("key", "ë")
    toml.NewEncoder(os.Stdout).SetMeta(meta).Encode(struct {
            Key string `toml:"key"`

    Would write:

    # A codepoint.
    key = 0x00eb  # ë

    It also has Key() to set both:

        Key("key", toml.Int{Width: 4, Base: 16}, toml.Doc("A codepoint"), toml.Comment("ë")).
        Key("other", toml.Comment("..."))

    The advantage of this is that it reduces the number of times you have to type the key string to 1, but it uses interface{}. Not yet decided which one I'll stick with, and also not a huge fan of Doc() and Comment(), but I can't really think of anything clearer at the moment (these are the names the Go ast uses).

    The Decode() sets all this information on the MetaData, so this:

    meta, _ := toml.Decode(..)

    Will write it out as "key = 0x00eb" again, rather than "key = 235".

    This way, pretty much any flag can be added programmatically without getting in the way of JSON/YAML/whatnot encoding/decoding.

    I don't especially care how you need to pass the keys as strings, but there isn't really any good way to do it otherwise. There is also the problem that the "key" as found in the parser may be different than the "key" the user is expecting if you don't use toml struct tags:

    type X struct { Key int }

    Will read "key = 2" in to "Key", but when encoding it will write as "Key" rather than "key". The type information will be set to "key", but when encoding it will look for "Key", so round-tripping won't work correct and has the potential for confusion if the wrong key is set.

    This is not so easy to fix since we don't have access to the struct in the parser. I think it's fine to just document this as a caveat and tell people to use struct tags, which is a good idea in any case.

    I'm not necessarily opposed to also adding struct tags for most of these things, although I'm not a huge fan of them. Since struct tags can't be set programmatically it's not really suitable for many use cases (e.g. setting comments dynamically, using multiline strings only if the string contains newlines, etc.) It's something that could maybe be added in a future PR, if a lot of people ask for it.

    Fixes #64 Fixes #75 Fixes #160 Fixes #192 Fixes #213 Fixes #269

  • Require 2-digit hour for local time

    Require 2-digit hour for local time

    Given a=1:32:00, this package returns a valid time-local value instead of returning an error.


    toml.Unmarshal([]byte("a=1:32:00"), &v)

    Or using the toml-test decoder:

    $ echo "a=1:32:00" | toml-test-decoder2
      "a": {
        "type": "time-local",
        "value": "01:32:00"
  • support round tripping with comments

    support round tripping with comments

    This is related to #75 but the solution might look quite different.

    TOML is being used for the dep tool, and a major obstacle to making that tool easier to use is the fact that the Gopkg.toml file cannot be programmatically manipulated without loss of user-edited content.

    If the toml package supported round-tripping the TOML while retaining comments, I believe that it would help a great deal.

    I'm afraid I'm sure how the API would look though. Some degree of loss is probably inevitable, but it may be possible to a job that's good enough for all practical cases.

Related tags
omniparser: a native Golang ETL streaming parser and transform library for CSV, JSON, XML, EDI, text, etc.
omniparser: a native Golang ETL streaming parser and transform library for CSV, JSON, XML, EDI, text, etc.

omniparser Omniparser is a native Golang ETL parser that ingests input data of various formats (CSV, txt, fixed length/width, XML, EDI/X12/EDIFACT, JS

Jan 4, 2023
User agent string parser in golang

User agent parsing useragent is a library written in golang to parse user agent strings. Usage First install the library with: go get

Aug 2, 2021
Freestyle xml parser with golang

fxml - FreeStyle XML Parser This package provides a simple parser which reads a XML document and output a tree structure, which does not need a pre-de

Jul 1, 2022
A simple json parser built using golang

jsonparser A simple json parser built using golang Installation: go get -u githu

Dec 29, 2021
A NMEA parser library in pure Go

go-nmea This is a NMEA library for the Go programming language (Golang). Features Parse individual NMEA 0183 sentences Support for sentences with NMEA

Dec 20, 2022
A shell parser, formatter, and interpreter with bash support; includes shfmt

sh A shell parser, formatter, and interpreter. Supports POSIX Shell, Bash, and mksh. Requires Go 1.14 or later. Quick start To parse shell scripts, in

Dec 29, 2022
A simple CSS parser and inliner in Go

douceur A simple CSS parser and inliner in Golang. Parser is vaguely inspired by CSS Syntax Module Level 3 and corresponding JS parser. Inliner only p

Dec 12, 2022
Simple HCL (HashiCorp Configuration Language) parser for your vars.

HCL to Markdown About To write a good documentation for terraform module, quite often we just need to print all our input variables as a fancy table.

Dec 14, 2021
A markdown parser written in Go. Easy to extend, standard(CommonMark) compliant, well structured.

goldmark A Markdown parser written in Go. Easy to extend, standards-compliant, well-structured. goldmark is compliant with CommonMark 0.29. Motivation

Dec 29, 2022
Unified diff parser and printer for Go

go-diff Diff parser and printer for Go. Installing go get -u Usage It doesn't actually compute a diff. It only rea

Dec 14, 2022
A PDF renderer for the goldmark markdown parser.
A PDF renderer for the goldmark markdown parser.

goldmark-pdf goldmark-pdf is a renderer for goldmark that allows rendering to PDF. Reference See

Jan 7, 2023
Experimental parser Angular template

Experimental parser Angular template This repository only shows what a parser on the Go might look like Benchmark 100k line of template Parser ms @ang

Dec 15, 2021
A dead simple parser package for Go
A dead simple parser package for Go

A dead simple parser package for Go V2 Introduction Tutorial Tag syntax Overview Grammar syntax Capturing Capturing boolean value Streaming Lexing Sta

Dec 30, 2022
An extension to the Goldmark Markdown Parser

Goldmark-Highlight An extension to the Goldmark Markdown Parser which adds parsing / rendering capabilities for rendering highlighted text. Highlighte

May 25, 2022
A parser combinator library for Go.

Takenoco A parser combinator library for Go. Examples CSV parser Dust - toy scripting language Usage Define the parser: package csv import ( "err

Oct 30, 2022
Quick and simple parser for PFSense XML configuration files, good for auditing firewall rules

pfcfg-parser version 0.0.1 : 13 January 2022 A quick and simple parser for PFSense XML configuration files to generate a plain text file of the main c

Jan 13, 2022
Interpreted Programming Language built in Go. Lexer, Parser, AST, VM.

Gago | Programming Language Built in Go if you are looking for the docs, go here Gago is a interpreted programming language. It is fully written in Go

May 6, 2022
[Crawler/Scraper for Golang]🕷A lightweight distributed friendly Golang crawler framework.一个轻量的分布式友好的 Golang 爬虫框架。

Goribot 一个分布式友好的轻量的 Golang 爬虫框架。 完整文档 | Document !! Warning !! Goribot 已经被迁移到 Gospider|。修复了一些调度问题并分离了网络请求部分到另一个仓库。此仓库会继续

Oct 29, 2022
golang 在线预览word,excel,pdf,MarkDown(Online Preview Word,Excel,PPT,PDF,Image by Golang)
golang 在线预览word,excel,pdf,MarkDown(Online Preview Word,Excel,PPT,PDF,Image by Golang)

Go View File 在线体验地址 (不会经常更新,保留最基本的预览功能。服务器配置较低,如果出现链接超时请等待几秒刷新重试,或者换Chrome) 目前已经完成 docker部署 (不用为运行环境烦恼) Wor

Dec 26, 2022