kdl-go is a Go library for version 1 of the KDL Document Language. It supports encoding and decoding KDL documents, marshaling and unmarshaling them into Go structs.
- supports all KDLv1 language features and passes all of the official KDLv1 test cases as of time of writing
- designed with performance and usability in mind
- familiar API and tag syntax, similar to
encoding/json - supports marshaling/unmarshaling into Go structures with support for
encoding.Text(Un)Marshalerand its own custom marshal/unmarshal interfaces - support for
encoding/json/v2-styleformatoptions fortime.Time,time.Duration,[]byte, andfloat32/64 - contextual errors, including the line and column of each error and a sample line displaying the error location
import "github.com/sblinch/kdl-go"Parse() decodes KDL to a *document.Document:
data := `
name "Bob"
age 76
active true
`
if doc, err := kdl.Parse(strings.NewReader(data)); err == nil {
// print the top-level nodes
for _, node := range doc.Nodes {
fmt.Println(node.Name.String())
}
}// output
name
age
activeGenerate() generates KDL from a *document.Document:
data := `
name "Bob"
age 76
active true
`
if doc, err := Parse(strings.NewReader(data)); err == nil {
// output the KDL representation of doc to stdout
if err := Generate(doc, os.Stdout); err != nil {
panic(err)
}
}// output:
name "Bob"
age 76
active trueUnmarshal() unmarshals KDL to a Go map or struct. The kdl tag can be used to map KDL node names to struct fields
or otherwise change unmarshaling behavior:
type Person struct {
Name string `kdl:"name"`
Age int `kdl:"age"`
Active bool `kdl:"active"`
}
data := `
name "Bob"
age 76
active true
`
var person Person
if err := kdl.Unmarshal(data, &person); err == nil {
fmt.Printf("%+v\n",person)
}// output
Person{
Name: "Bob",
Age: 76,
Active: true
}kdl-go's unmarshaler is described in detail in Unmarshaling in kdl-go.
Use kdl.NewDecoder() to create a new KDL decoder whose options can be customized to your needs:
type Person struct {
Name string `kdl:"name"`
Age int `kdl:"age"`
Active bool `kdl:"active"`
}
data := `
name "Bob"
age 76
active true
geriatric true
`
var person Person
dec := kdl.NewDecoder(strings.NewReader(data))
// ignore the unhandled "geriatric" node
dec.Options.AllowUnhandledNodes = true
if err := dec.Decode(&person); err == nil {
fmt.Printf("%+v\n", person)
}// output
Person{
Name: "Bob",
Age: 76,
Active: true
}Marshal() marshals a Go map or struct into KDL. The kdl tag can be used to map struct fields to KDL node names
or otherwise change marshaling behavior:
type Person struct {
Name string `kdl:"name"`
Age int `kdl:"age"`
Active bool `kdl:"active"`
}
person := Person{
Name: "Bob",
Age: 32,
Active: true,
}
if data, err := kdl.Marshal(person); err == nil {
fmt.Println(string(data))
}// output:
name "Bob"
age 32
active truekdl-go's marshaler is described in detail in Marshaling in kdl-go.
Use kdl.NewEncoder() to create a new KDL encoder whose options can be customized to your needs:
type Person struct {
Name string `kdl:"name"`
Age int `kdl:"age"`
Active bool `kdl:"active"`
}
person := Person{
Name: "Bob Jones",
Age: 32,
Active: true,
}
enc := kdl.NewEncoder(os.Stdout)
if err := enc.Encode(person); err != nil {
panic(err)
}//output
name "Bob Jones"
age 32
active truekdl-go can also parse nginx-style configuration files using its relaxed.NGINXSyntax mode:
data := `
# web root
location / {
root /var/www/html;
}
# a missing location
location /missing {
return 404;
}
`
type Location struct {
Root string `kdl:"root,omitempty,child"`
Return int `kdl:"return,omitempty,child"`
}
type NginxServer struct {
Locations map[string]Location `kdl:"location,multiple"`
}
var ngx NginxServer
dec := kdl.NewDecoder(strings.NewReader(data))
dec.Options.RelaxedNonCompliant |= relaxed.NGINXSyntax
if err := dec.Decode(&ngx); err == nil {
fmt.Printf("%#v\n", ngx)
}// output:
NginxServer{
Locations: {
"/": { Root:"/var/www/html", Return:0 },
"/missing": { Root:"", Return:404 }
}
}See the unmarshaling docs for further information.
To download and test against all Full Document Test Cases from the kdl.org repository, run:
git clone https://github.com/sblinch/kdl-go
cd kdl-go
git clone https://github.com/kdl-org/kdl kdl-org
cd internal/parser
go test -v -run TestKDLOrgTestCases -tags kdldeterministicAs of October 2023, kdl-go passes all of the available test cases.
kdl-go is actively maintained and is has been used as a configuration unmarshaler in a number of production applications for several years now. It is considered stable at this time.
Issue reports and pull requests are welcome.
kdl-go implements the KDLv1 specification only.
The KDLv2 specification introduces some changes that this maintainer is not comfortable implementing as explained in issue #6. Similar concerns have been raised by other users as well (eg: here and here). As such, support for KDLv2 is not planned at this time.
kdl-go is released under the MIT license. See LICENSE for details.