9

As title, I want to know how to use toml files from golang.

Before that, I show my toml examples. Is it right?

[datatitle]
enable = true
userids = [
    "12345", "67890"
]
    [datatitle.12345]
    prop1 = 30
    prop2 = 10

    [datatitle.67890]
    prop1 = 30
    prop2 = 10

And then, I want to set these data as type of struct.

As a result I want to access child element as below.

datatitle["12345"].prop1
datatitle["67890"].prop2

Thanks in advance!

fedorqui
  • 275,237
  • 103
  • 548
  • 598
Harry
  • 1,257
  • 3
  • 14
  • 25

6 Answers6

10

First get BurntSushi's toml parser:

go get github.com/BurntSushi/toml

BurntSushi parses toml and maps it to structs, which is what you want.

Then execute the following example and learn from it:

package main

import (
    "github.com/BurntSushi/toml"
    "log"
)

var tomlData = `title = "config"
[feature1]
enable = true
userids = [
  "12345", "67890"
]

[feature2]
enable = false`

type feature1 struct {
    Enable  bool
    Userids []string
}

type feature2 struct {
    Enable bool
}

type tomlConfig struct {
    Title string
    F1    feature1 `toml:"feature1"`
    F2    feature2 `toml:"feature2"`
}

func main() {
    var conf tomlConfig
    if _, err := toml.Decode(tomlData, &conf); err != nil {
        log.Fatal(err)
    }
    log.Printf("title: %s", conf.Title)
    log.Printf("Feature 1: %#v", conf.F1)
    log.Printf("Feature 2: %#v", conf.F2)
}

Notice the tomlData and how it maps to the tomlConfig struct.

See more examples at https://github.com/BurntSushi/toml

Zippo
  • 15,850
  • 10
  • 60
  • 58
  • Thanks for good advises! I'll try that and then feedback you! – Harry Dec 16 '15 at 10:43
  • 1
    @Harry I fixed the example, it didn't run well, and as far as I can see, BurnSushi/toml doesn't support dots in the category, like your question shows. If you must have those dots, try another library like https://github.com/pelletier/go-toml – Zippo Dec 16 '15 at 12:07
  • 1
    It was salved for your help!! I feedback my code when I'm free. Thanks a lot! – Harry Dec 17 '15 at 13:46
  • @Harry Glad I could help, and welcome to Stack Overflow :-). If this answer solved your issue, please mark it as accepted. – Zippo Dec 17 '15 at 13:52
  • 1
    Hello. I'm sorry to tell it, but the BurntSushi/toml is officially unmaintained anymore https://github.com/BurntSushi/toml/commit/ea60c4def909bde529d41a7e0674e31eba751da3 – Felixoid May 04 '21 at 10:26
4

A small update for the year 2019 - there is now newer alternative to BurntSushi/toml with a bit richer API to work with .toml files:

pelletier/go-toml (and documentation)

For example having config.toml file (or in memory):

[postgres]
user = "pelletier"
password = "mypassword"

apart from regular marshal and unmarshal of the entire thing into predefined structure (which you can see in the accepted answer) with pelletier/go-toml you can also query individual values like this:

config, err := toml.LoadFile("config.toml")

if err != nil {
    fmt.Println("Error ", err.Error())
} else {

    // retrieve data directly

    directUser := config.Get("postgres.user").(string)
    directPassword := config.Get("postgres.password").(string)
    fmt.Println("User is", directUser, " and password is", directPassword)

    // or using an intermediate object

    configTree := config.Get("postgres").(*toml.Tree)
    user := configTree.Get("user").(string)
    password := configTree.Get("password").(string)
    fmt.Println("User is", user, " and password is", password)

    // show where elements are in the file

    fmt.Printf("User position: %v\n", configTree.GetPosition("user"))
    fmt.Printf("Password position: %v\n", configTree.GetPosition("password"))

    // use a query to gather elements without walking the tree

    q, _ := query.Compile("$..[user,password]")
    results := q.Execute(config)
    for ii, item := range results.Values() {
        fmt.Println("Query result %d: %v", ii, item)
    }
}

UPDATE

There is also spf13/viper that works with .toml config files (among other supported formats), but it might be a bit overkill in many cases.

UPDATE 2

Viper is not really an alternative (credits to @GoForth).

Sevenate
  • 6,221
  • 3
  • 49
  • 75
  • 1
    Actually Viper uses pelletier's library: https://github.com/spf13/viper/blob/master/viper.go#L43 – GoForth Apr 20 '21 at 21:28
  • 1
    @GoForth that is good to know, thanks! I guess then we can strike viper out from the question :) – Sevenate Apr 20 '21 at 23:13
  • 1
    Sure, I was actually surprised myself when I looked into it. I still think Viper is cool as it's an all-in-one solution I just wanted to point that out in case someone is looking only for TOML. – GoForth Apr 21 '21 at 03:18
3

This issue was solved using recommended pkg BurntSushi/toml!! I did as below and it's part of code.

[toml example]

[title]
enable = true
[title.clientinfo.12345]
distance = 30
some_id = 6

[Golang example]

type TitleClientInfo struct {
    Distance int    `toml:"distance"`
    SomeId  int     `toml:"some_id"`
}

type Config struct {
    Enable     bool     `toml:"enable"`
    ClientInfo map[string]TitleClientInfo `toml:"clientinfo"`
}

var config Config
_, err := toml.Decode(string(d), &config)

And then, it can be used as I expected.

config.ClientInfo[12345].Distance

Thanks!

Harry
  • 1,257
  • 3
  • 14
  • 25
2

With solution Viper you can use a configuration file in JSON, TOML, YAML, HCL, INI and others properties formats.

Create file:

./config.toml

First import:

import (config "github.com/spf13/viper")

Initialize:

config.SetConfigName("config")
config.AddConfigPath(".")
err := config.ReadInConfig()
if err != nil {             
    log.Println("ERROR", err.Error())
}

And get the value:

config.GetString("datatitle.12345.prop1")
config.Get("datatitle.12345.prop1").(int32)

Doc.: https://github.com/spf13/viper

e.g.: https://repl.it/@DarlanD/Viper-Examples#main.go

Darlan Dieterich
  • 2,369
  • 1
  • 27
  • 37
0

I am using spf13/viper

3rd packages

Status Project Starts Forks
Alive spf13/viper stars stars
Alive BurntSushi/toml stars stars

usage of viper

I tried to use a table to put the code and the contents of the configuration file together, but obviously, the editing did not match the final result, so I put the image up in the hope that it would make it easier for you to compare

enter image description here


package main
import (
    "github.com/spf13/viper"
    "log"
    "os"
)
func main() {
    check := func(err error) {
        if err != nil {
            panic(err)
        }
    }
    myConfigPath := "test_config.toml"
    fh, err := os.OpenFile(myConfigPath, os.O_RDWR, 0666)
    check(err)
    viper.SetConfigType("toml") // do not ignore
    err = viper.ReadConfig(fh)
    check(err)

    // Read
    log.Printf("%#v", viper.GetString("title"))                 // "my config"
    log.Printf("%#v", viper.GetString("DataTitle.12345.prop1")) // "30"
    log.Printf("%#v", viper.GetString("dataTitle.12345.prop1")) // "30"  // case-insensitive
    log.Printf("%#v", viper.GetInt("DataTitle.12345.prop1"))    // 30
    log.Printf("%#v", viper.GetIntSlice("feature1.userids"))    // []int{456, 789}

    // Write
    viper.Set("database", "newuser")
    viper.Set("owner.name", "Carson")
    viper.Set("feature1.userids", []int{111, 222}) // overwrite
    err = viper.WriteConfigAs(myConfigPath)
    check(err)
}
title = "my config"

[datatitle]

  [datatitle.12345]
    prop1 = 30

[feature1]
  userids = [456,789]

database = "newuser"  # New
title = "my config"

[datatitle]

  [datatitle.12345]
    prop1 = 30

[feature1]
  userids = [111,222]  # Update

[owner]  # New
  name = "Carson"

Carson
  • 6,105
  • 2
  • 37
  • 45
  • 1
    BurntSushi/toml has had a new maintainer recently https://github.com/BurntSushi/toml/issues/272 it is not unmaitained anymore –  Sep 05 '21 at 14:15
  • 1
    Hi @mh-cbon, thank you for the reminder. [Unmaintained is create on 2020-10-18 by Andrew Gallant](https://github.com/BurntSushi/toml/blob/ea60c4def909bde529d41a7e0674e31eba751da3/README.md). It has been [removed by Martin Tournoij on 2021-06-08](https://github.com/BurntSushi/toml/commit/0179a136e5e6f2a56#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5) – Carson Sep 05 '21 at 17:48
0

I am using this [1] go-toml library.

It works great for my uses. I wrote this [2] go util to deal with containerd config.toml file using go-toml

[1]https://github.com/pelletier/go-toml

[2]https://github.com/prakashmirji/toml-configer

  • 1
    Please provide additional details in your answer. As it's currently written, it's hard to understand your solution. – Community Sep 03 '21 at 12:48