Encodes Go values to TOON (Token-Oriented Object Notation) and decodes TOON back. TOON is a compact, indentation-based encoding of the JSON data model for LLM input.
go get github.com/toon-format/toon-go
package main
import (
"fmt"
"github.com/toon-format/toon-go"
)
type User struct {
ID int `toon:"id"`
Name string `toon:"name"`
Role string `toon:"role"`
}
type Payload struct {
Users []User `toon:"users"`
}
func main() {
in := Payload{Users: []User{{1, "Ada", "admin"}, {2, "Bob", "user"}}}
encoded, err := toon.MarshalString(in)
if err != nil {
panic(err)
}
fmt.Println(encoded)
// users[2]{id,name,role}:
// 1,Ada,admin
// 2,Bob,user
var out Payload
if err := toon.UnmarshalString(encoded, &out); err != nil {
panic(err)
}
fmt.Printf("%+v\n", out)
// {Users:[{ID:1 Name:Ada Role:admin} {ID:2 Name:Bob Role:user}]}
}
Without a destination type, Decode and DecodeString return map[string]any, []any, and primitives. Pass encoder options to Marshal and decoder options to Unmarshal or Decode:
| Option | Default | Description |
|---|---|---|
WithIndent(n) | 2 | Spaces per indentation level when encoding |
WithDelimiter(d) | DelimiterComma | Delimiter for array values and tabular rows: DelimiterComma, DelimiterTab, or DelimiterPipe |
WithTimeFormatter(f) | RFC 3339 in UTC | Formats time.Time values |
WithStrictMode(b) | true | false accepts declared counts that don't match, duplicate keys (the last wins), tab or misaligned indentation, blank lines inside an array or keyed tabular object, and a block indented too deep; every other decoding error stays |
WithDecoderIndent(n) | 2 | Expected spaces per indentation level when decoding |
Targets TOON spec v4.4, and the test suite runs the spec's conformance fixtures.
float64 – integers beyond 2^53 lose precision and a token that overflows float64 (e.g. 1e999) decodes as a string; on encode, integers beyond ±(2^53 − 1), *big.Int values included, become quoted decimal strings (§4)toon tags – toon:"name" renames, toon:"name,omitempty" skips zero values, toon:"-" skips the field, a field without a toon tag falls back to its json tag, and an untagged field uses its Go name; maps need string keys, time.Time and fmt.Stringer values become strings, and NaN and ±Inf become null (§3)map[string]any, so document key order is lost – on encode, toon.Object keeps its field order and Go maps are written in sorted key order (§2)Releases follow SemVer: a new spec MINOR version ships as a MINOR release, even when it changes how hand-written input decodes, and a MAJOR release means an API break or a new spec MAJOR version.
See CONTRIBUTING.md for the development setup and pull request guidelines.
MIT License © 2025-PRESENT Bintang Pradana Erlangga Putra and Johann Schopplich
214 followers · starred Nov 2025
541 followers · starred Mar 2026
29 followers · starred Nov 2025
188 followers · starred Mar 2026
Encodes Go values to TOON (Token-Oriented Object Notation) and decodes TOON back. TOON is a compact, indentation-based encoding of the JSON data model for LLM input.
go get github.com/toon-format/toon-go
package main
import (
"fmt"
"github.com/toon-format/toon-go"
)
type User struct {
ID int `toon:"id"`
Name string `toon:"name"`
Role string `toon:"role"`
}
type Payload struct {
Users []User `toon:"users"`
}
func main() {
in := Payload{Users: []User{{1, "Ada", "admin"}, {2, "Bob", "user"}}}
encoded, err := toon.MarshalString(in)
if err != nil {
panic(err)
}
fmt.Println(encoded)
// users[2]{id,name,role}:
// 1,Ada,admin
// 2,Bob,user
var out Payload
if err := toon.UnmarshalString(encoded, &out); err != nil {
panic(err)
}
fmt.Printf("%+v\n", out)
// {Users:[{ID:1 Name:Ada Role:admin} {ID:2 Name:Bob Role:user}]}
}
Without a destination type, Decode and DecodeString return map[string]any, []any, and primitives. Pass encoder options to Marshal and decoder options to Unmarshal or Decode:
| Option | Default | Description |
|---|---|---|
WithIndent(n) | 2 | Spaces per indentation level when encoding |
WithDelimiter(d) | DelimiterComma | Delimiter for array values and tabular rows: DelimiterComma, DelimiterTab, or DelimiterPipe |
WithTimeFormatter(f) | RFC 3339 in UTC | Formats time.Time values |
WithStrictMode(b) | true | false accepts declared counts that don't match, duplicate keys (the last wins), tab or misaligned indentation, blank lines inside an array or keyed tabular object, and a block indented too deep; every other decoding error stays |
WithDecoderIndent(n) | 2 | Expected spaces per indentation level when decoding |
Targets TOON spec v4.4, and the test suite runs the spec's conformance fixtures.
float64 – integers beyond 2^53 lose precision and a token that overflows float64 (e.g. 1e999) decodes as a string; on encode, integers beyond ±(2^53 − 1), *big.Int values included, become quoted decimal strings (§4)toon tags – toon:"name" renames, toon:"name,omitempty" skips zero values, toon:"-" skips the field, a field without a toon tag falls back to its json tag, and an untagged field uses its Go name; maps need string keys, time.Time and fmt.Stringer values become strings, and NaN and ±Inf become null (§3)map[string]any, so document key order is lost – on encode, toon.Object keeps its field order and Go maps are written in sorted key order (§2)Releases follow SemVer: a new spec MINOR version ships as a MINOR release, even when it changes how hand-written input decodes, and a MAJOR release means an API break or a new spec MAJOR version.
See CONTRIBUTING.md for the development setup and pull request guidelines.
MIT License © 2025-PRESENT Bintang Pradana Erlangga Putra and Johann Schopplich
214 followers · starred Nov 2025
541 followers · starred Mar 2026
29 followers · starred Nov 2025
188 followers · starred Mar 2026