tojson

package module
v0.0.0-...-2062b77 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2026 License: MIT Imports: 6 Imported by: 0

README

tojson

Parse YAML, TOML, JSON variants, and document front matter into standard JSON bytes. Zero dependencies, stdlib only.

Go Reference Build Status

Why

  • One library for the configuration and front matter formats you are most likely to encounter.
  • Zero dependencies. tojson uses the Go standard library only.
  • Convert everything to JSON bytes, then use the normal Go JSON ecosystem for unmarshaling, validation, and downstream tooling.
  • No custom marshaling layer. Use json struct tags only.
  • Standardized API and error handling across all supported formats.

Typical use cases:

  • High-performance, minimal-dependency YAML config decoding.
  • Static site and content pipelines: parse front matter, decode the metadata, pass the body to a renderer.
  • Accepting "better JSON" that allows comments and trailing commas.
  • Normalizing obsolete JSON variants and recovering broken configurations.

Quick Start

Requires Go 1.24+.

go get github.com/client9/tojson
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/client9/tojson"
)

type Article struct {
	Title  string `json:"title"`
	Author string `json:"author"`
	Draft  bool   `json:"draft"`
}

func main() {
	src := []byte("title: hello-world\nauthor: alice\ndraft: false\n")

	raw, err := tojson.FromYAML(src)
	if err != nil {
		log.Fatal(err)
	}

	var article Article
	if err := json.Unmarshal(raw, &article); err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%+v\n", article)
}

API

tojson.FromJSONVariant(src []byte) ([]byte, error)
tojson.FromYAML(src []byte) ([]byte, error)
tojson.FromTOML(src []byte) ([]byte, error)
tojson.FromFrontMatter(src []byte) (meta []byte, body []byte, err error)

FromJSONVariant, FromYAML, and FromTOML return compact JSON on success. FromFrontMatter returns compact JSON metadata and the raw body bytes; meta is nil when no front matter is present.

Error Handling

Parse failures are returned as *tojson.ParseError, which includes a 1-based line number and a 1-based column number where the failure occurred.

_, err := tojson.FromJSONVariant([]byte("{ unclosed: [1, 2, }"))
if err != nil {
	var pe *tojson.ParseError
	if errors.As(err, &pe) {
		log.Printf("parse error at line %d, col %d: %s", pe.Line, pe.Column, pe.Message)
	}
}

Examples

JSON variants
src := []byte(`
{
  // comments are allowed
  unquoted: 'value',
  hex: 0x2a,
  trailing: [1, 2, 3,],
}
`)

raw, err := tojson.FromJSONVariant(src)
if err != nil {
	log.Fatal(err)
}

// raw == {"unquoted":"value","hex":42,"trailing":[1,2,3]}
YAML
type Article struct {
	Title string   `json:"title"`
	Tags  []string `json:"tags"`
}

src := []byte(`
title: Hello
tags:
  - go
  - yaml
`)

raw, err := tojson.FromYAML(src)
if err != nil {
	log.Fatal(err)
}

var article Article
if err := json.Unmarshal(raw, &article); err != nil {
	log.Fatal(err)
}
TOML
type Article struct {
	Title  string `json:"title"`
	Author string `json:"author"`
}

src := []byte(`
title = "hello-world"
author = "alice"
`)

raw, err := tojson.FromTOML(src)
if err != nil {
	log.Fatal(err)
}

var article Article
if err := json.Unmarshal(raw, &article); err != nil {
	log.Fatal(err)
}
Front matter
type Article struct {
	Title  string `json:"title"`
	Author string `json:"author"`
}

src := []byte(`---
title: Hello World
author: alice
---
This is the body.
`)

meta, body, err := tojson.FromFrontMatter(src)
if err != nil {
	log.Fatal(err)
}

if meta != nil {
	var article Article
	if err := json.Unmarshal(meta, &article); err != nil {
		log.Fatal(err)
	}
}

// body == []byte("This is the body.\n")
_ = body

Supported Inputs

FromJSONVariant handles JSON5, JWCC, HuJSON, JSONC, and HanSON-style inputs: comments, trailing commas, unquoted keys, single-quoted strings, hex literals, and more. FromYAML supports a practical subset covering mappings, sequences, scalars, and block strings — not anchors, tags, or complex keys. FromTOML accepts valid TOML. FromFrontMatter detects the format from the opening sentinel (---, +++, {, or qualified variants like ---toml).

See docs/supported-inputs.md for the full breakdown.

Performance

On frontmatter-style benchmark inputs in this repo, FromYAML used substantially less memory and was several times faster than common Go YAML packages. FromTOML used about half the memory of the TOML packages tested, with speed roughly comparable to pelletier/go-toml and faster than BurntSushi/toml.

See docs/performance.md for benchmark methodology, exact library comparisons, and raw numbers.

CLI

The repo also includes a tojson command for testing and scripting:

go install github.com/client9/tojson/cmd/tojson@latest

tojson file.yaml
tojson file.toml
tojson file.json5
cat file.yaml | tojson -f yaml
tojson -pretty file.yaml

Use -f when reading from stdin so the input format is explicit.

License

MIT. See LICENSE.txt

Documentation

Overview

Package tojson converts YAML, TOML, and JSON variants to standard JSON bytes.

The package converts source bytes to JSON, then they can be unmarshalled using standard json struct tags.

raw, err := tojson.FromYAML(src)
if err != nil { ... }
if err = json.Unmarshal(raw, &cfg); err != nil { ... }

The package exposes four top-level functions:

tojson.FromJSONVariant(src []byte) ([]byte, error)
tojson.FromYAML(src []byte) ([]byte, error)
tojson.FromTOML(src []byte) ([]byte, error)
tojson.FromFrontMatter(src []byte) (meta []byte, body []byte, err error)

FromYAML intentionally supports a practical YAML subset for config files and front matter, not the full YAML specification.

FromFrontMatter handles documents that embed metadata in a front matter block before the main content, as used by Hugo, Jekyll, and similar static site generators. It detects the format from the opening sentinel and returns the metadata as JSON and the body separately:

meta, body, err := tojson.FromFrontMatter(src)
if err != nil { ... }
if meta != nil {
	if err := json.Unmarshal(meta, &article); err != nil { ... }
}
// use body (markdown, etc.)

Parse failures are returned as *ParseError, which carries a 1-based line and column number and can be inspected with errors.As:

var pe *tojson.ParseError
if errors.As(err, &pe) {
	fmt.Printf("line %d, column %d: %s\n", pe.Line, pe.Column, pe.Message)
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FromFrontMatter

func FromFrontMatter(in []byte) (meta []byte, body []byte, err error)

FromFrontMatter splits a document into a front matter block and a body, converts the front matter to JSON, and returns both.

Supported opening/closing sentinel pairs:

---     / ---   YAML
---yaml / ---   YAML (explicit qualifier)
---toml / ---   TOML (explicit qualifier)
---json / ---   JSON (explicit qualifier)
+++     / +++   TOML
{       / }     JSON (each sentinel must be the only character on its line)

Trailing whitespace on sentinel lines is ignored on both open and close, since invisible characters cause hard-to-diagnose authoring errors.

A ---<qualifier> opening that is not one of the recognised qualifiers above returns an error rather than silently treating the file as having no front matter, so typos like "---yml" are caught immediately.

A missing closing sentinel is an error. Silently returning no metadata would risk leaking private front matter fields into the document body; silently parsing the entire file as metadata would produce a confusing error far from the actual problem.

On success, meta is compact JSON and body is the bytes after the closing sentinel line. If no recognised front matter opening is detected, meta is nil, body is the full input, and err is nil. Parse failures inside the front matter block are returned as *ParseError.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/client9/tojson"
)

func main() {
	type Article struct {
		Title  string `json:"title"`
		Author string `json:"author"`
	}

	src := []byte("---\ntitle: Hello World\nauthor: Alice\n---\nThis is the body.\n")

	meta, body, err := tojson.FromFrontMatter(src)
	if err != nil {
		panic(err)
	}

	var article Article
	if err := json.Unmarshal(meta, &article); err != nil {
		panic(err)
	}

	fmt.Printf("title: %s\n", article.Title)
	fmt.Printf("author: %s\n", article.Author)
	fmt.Printf("body: %s", body)
}
Output:
title: Hello World
author: Alice
body: This is the body.

func FromJSONVariant

func FromJSONVariant(src []byte) ([]byte, error)

FromJSONVariant converts JSON and common JSON-derived variants to standard JSON. It handles JSON5/HuJSON/JWCC/JSONC/HanSON features such as trailing/leading commas, line and block comments, unquoted keys, single-quoted and backtick strings, and hex literals.

Example
package main

import (
	"fmt"

	"github.com/client9/tojson"
)

func main() {
	src := []byte(`
{
  // comments are allowed
  unquoted: 'value',
  hex: 0x2a,
  trailing: [1, 2, 3,],
}
`)

	raw, err := tojson.FromJSONVariant(src)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(raw))
}
Output:
{"unquoted":"value","hex":42,"trailing":[1,2,3]}

func FromTOML

func FromTOML(src []byte) ([]byte, error)

FromTOML converts TOML to standard JSON. The output can be passed directly to encoding/json.Unmarshal using only json struct tags.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/client9/tojson"
)

func main() {
	type Article struct {
		Title  string `json:"title"`
		Author string `json:"author"`
	}

	src := []byte("title = \"hello-world\"\nauthor = \"alice\"\n")

	raw, err := tojson.FromTOML(src)
	if err != nil {
		panic(err)
	}

	var article Article
	if err := json.Unmarshal(raw, &article); err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", article)
}
Output:
{Title:hello-world Author:alice}

func FromYAML

func FromYAML(src []byte) ([]byte, error)

FromYAML converts a YAML subset to standard JSON. The output can be passed directly to encoding/json.Unmarshal using only json struct tags. Anchors/aliases, tags, and complex keys are not supported.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/client9/tojson"
)

func main() {
	type Article struct {
		Title  string `json:"title"`
		Author string `json:"author"`
		Draft  bool   `json:"draft"`
	}

	src := []byte("title: hello-world\nauthor: alice\ndraft: false\n")

	raw, err := tojson.FromYAML(src)
	if err != nil {
		panic(err)
	}

	var article Article
	if err := json.Unmarshal(raw, &article); err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", article)
}
Output:
{Title:hello-world Author:alice Draft:false}

Types

type ParseError

type ParseError struct {
	Line    int    // 1-based line number in the original input
	Column  int    // 1-based column number
	Message string // description of the problem
}

ParseError is returned by all From* functions when the input cannot be parsed. Line and Column are always 1-based.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/client9/tojson"
)

func main() {
	_, err := tojson.FromJSONVariant([]byte("{ unclosed: [1, 2, }"))
	if err != nil {
		var pe *tojson.ParseError
		if errors.As(err, &pe) {
			fmt.Printf("line %d, column %d: %s\n", pe.Line, pe.Column, pe.Message)
		}
	}
}
Output:
line 1, column 20: unmatched object end, level=2, stack="{["

func (*ParseError) Error

func (e *ParseError) Error() string

Directories

Path Synopsis
cmd
tojson command
tojson converts YAML/TOML/JSON variants and front matter from a file or stdin to JSON on stdout.
tojson converts YAML/TOML/JSON variants and front matter from a file or stdin to JSON on stdout.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL