A Go package for encoding and decoding JSON5 data. The API mirrors the standard library encoding/json package, making it a drop-in replacement for projects that need JSON5 support.
JSON5 is a superset of JSON that adds several features from ECMAScript 5.1:
- Comments — single-line (
//) and multi-line (/* */) - Trailing commas — in objects and arrays
- Unquoted object keys — any valid ECMAScript identifier
- Single-quoted strings —
'like this' - Multiline strings — using backslash line continuation
- Extended escape sequences —
\x41,\v,\0 - Hexadecimal numbers —
0xFF - Infinity and NaN — as number literals
- Leading/trailing decimal points —
.5and5. - Positive sign on numbers —
+42 - Extended whitespace — vertical tab, form feed, BOM, non-breaking space, and Unicode Zs category
Every valid JSON document is also valid JSON5.
go get github.com/alchemy/json5
Parse JSON5 data into Go values:
package main
import (
"fmt"
"github.com/alchemy/json5"
)
func main() {
data := []byte(`{
// Database configuration
host: 'localhost',
port: 5432,
ssl: true,
maxConns: 0xFF,
timeout: .5, // seconds
tags: ['primary', 'fast',],
}`)
var config map[string]any
if err := json5.Unmarshal(data, &config); err != nil {
panic(err)
}
fmt.Println(config)
}Struct tags use json5 (with fallback to json):
type Config struct {
Host string `json5:"host"`
Port int `json5:"port"`
SSL bool `json5:"ssl"`
MaxConns int `json5:"maxConns"`
Timeout float64 `json5:"timeout"`
Tags []string `json5:"tags"`
Debug bool `json5:"debug,omitempty"`
}
var config Config
err := json5.Unmarshal(data, &config)Encode Go values to JSON5:
b, err := json5.Marshal(config)
// {"host":"localhost","port":5432,"ssl":true,"maxConns":255,"timeout":0.5,"tags":["primary","fast"]}Output is valid JSON for standard types. For float64 values of Infinity and NaN, the encoder outputs JSON5-specific literals:
b, _ := json5.Marshal(math.Inf(1)) // Infinity
b, _ = json5.Marshal(math.NaN()) // NaNPretty-print with indentation:
b, err := json5.MarshalIndent(config, "", " ")// Decode from a reader
dec := json5.NewDecoder(file)
dec.UseNumber() // decode numbers as json5.Number
dec.DisallowUnknownFields() // error on unknown struct fields
var config Config
err := dec.Decode(&config)
// Encode to a writer
enc := json5.NewEncoder(os.Stdout)
err = enc.Encode(config)if json5.Valid(data) {
fmt.Println("valid JSON5")
}Delay parsing of a JSON5 value:
type Event struct {
Type string `json5:"type"`
Payload json5.RawMessage `json5:"payload"`
}
var event Event
json5.Unmarshal(data, &event)
// Parse payload later based on type
switch event.Type {
case "click":
var click ClickPayload
json5.Unmarshal(event.Payload, &click)
}Preserve exact number representation. The Float64() and Int64() methods handle all JSON5 number formats including hexadecimal, Infinity, and NaN:
dec := json5.NewDecoder(reader)
dec.UseNumber()
var v any
dec.Decode(&v)
n := v.(json5.Number)
f, _ := n.Float64() // as float64 (handles 0xFF, Infinity, NaN)
i, _ := n.Int64() // as int64 (handles 0xFF)
s := n.String() // original textThe package recognizes json5 struct tags with the same syntax as encoding/json:
type Example struct {
Field1 string `json5:"name"` // custom name
Field2 int `json5:"count,omitempty"` // omit if zero value
Field3 bool `json5:",omitempty"` // default name, omit if zero
Field4 int `json5:"val,string"` // encode/decode as string
Field5 string `json5:"-"` // skip this field
}If no json5 tag is present, the package falls back to the json tag, enabling compatibility with existing types.
The package checks for these interfaces in order:
json5.Marshaler/json5.Unmarshaler— JSON5-specificjson.Marshaler/json.Unmarshaler— standard library compatibilityencoding.TextMarshaler/encoding.TextUnmarshaler
This means types that already implement json.Marshaler or json.Unmarshaler work without modification.
MIT