configjson

package module
v0.2.12 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 8 Imported by: 0

README

config-json

Read and write JSON and JSON Lines configuration through config, structure-preserving

Go Reference Pipeline Coverage phpboyscout Go toolkit

Part of the phpboyscout Go toolkit — small, framework-free Go modules extracted from go-tool-base. Documented with the parent module at config.go.phpboyscout.uk


config already reads a JSON document unaided, because YAML 1.2 is a superset of JSON and the core's codec is YAML. This module is for the two things the core cannot do: add a key to a JSON file and keep it valid JSON, and read JSON Lines. A consumer who needs either takes it, and a consumer who does not pays nothing for it.

import (
	"gitlab.com/phpboyscout/go/config"
	configjson "gitlab.com/phpboyscout/go/config-json"
)

store, err := config.NewStore(ctx,
	config.WithFiles(fsys, "/etc/app.yaml"),                    // YAML, the default
	config.WithBackend(configjson.New(fsys, "/etc/app.json")),  // outranks it
	config.WithBackend(configjson.NewLines(fsys, "/var/lib/app/state.jsonl")),
)

A JSON layer takes part in precedence, per-key merge, provenance and write routing exactly as a YAML file does. New reads a single JSON document; NewLines reads JSON Lines, one object per line.

Writing preserves the file

Re-serialising a decoded document alphabetises its keys and discards its formatting — the merged-view write the whole toolkit exists to avoid. So writes edit the document in place: an edit to an existing key changes one value and leaves every other byte, including key order and indentation, exactly as it was. A new key is inserted and the document re-indented to its own detected style. A single-line document stays single-line.

JSON has no comments to lose, so that is the whole contract: key order always survives; indentation is preserved on an edit and matched on an insertion. A top level that is not a JSON object is refused at load — it cannot carry configuration keys.

JSON Lines is several layers

One line is one document, the same model a multi-document YAML file uses: the line index is the document index, provenance distinguishes them (state.jsonl#1), and a later line outranks an earlier one. An edit targets a specific line and leaves the others byte-identical.

Dependencies

Reading is standard-library only. Writing adds tidwall/sjson and tidwall/pretty, and what those bring — asserted by an allowlist test, so an unforeseen transitive addition fails the build rather than arriving quietly. No filesystem library: you supply the config.FS.

License

See LICENSE.

Documentation

Overview

Package configjson reads and writes JSON and JSON Lines configuration through the config codec seam.

It is a sibling of the config core, not part of it: a consumer who needs JSON takes this module, and a consumer who does not pays nothing for it. The core's own codec is YAML, which reads a JSON document unaided because YAML 1.2 is a superset of JSON; this module is for what that cannot do, which is adding a key to a JSON file while keeping it JSON, and reading JSON Lines.

config.NewStore(ctx,
	config.WithFiles(fsys, "/etc/app.yaml"),        // YAML, the default
	config.WithBackend(configjson.New(fsys, "/etc/app.json")),
	config.WithBackend(configjson.NewLines(fsys, "/var/log/app.jsonl")),
)

A JSON layer takes part in precedence, per-key merge, provenance and write routing exactly as a YAML file does. Reading uses the standard library; only the write path takes a dependency, on tidwall/sjson and tidwall/pretty, to edit a document in place instead of re-serialising it — so an edit changes one value and leaves key order and the rest of the file alone.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(filesystem config.FS, path string) config.Backend

New reads and writes a single JSON document.

The document's top level must be a JSON object; an array or a scalar cannot carry configuration keys and is refused at load.

func NewLines

func NewLines(filesystem config.FS, path string) config.Backend

NewLines reads and writes JSON Lines — one JSON object per line.

One line is one document, mapping onto the seam's multi-document model exactly as YAML documents do: the line index is the document index, provenance distinguishes them, and a later line outranks an earlier one for the same key. Each line's top level must be a JSON object.

Types

type Codec added in v0.2.0

type Codec struct{}

Codec reads a single JSON document and edits it in place. New uses it, and it is exported so a backend adapter can reuse it as a value decoder — a Consul or etcd key whose value is a JSON object, for instance, decoded into a subtree via that adapter's WithValueCodec option. It is a config.Codec (and a config.EditingCodec).

Decoding is the standard library's; editing is sjson's, which changes the bytes for one key and leaves the rest untouched. The two never cross: decoded values feed the read side, and edits are applied to bytes, so a number read as float64 and a number written by sjson never have to agree — the seam re-decodes what was written.

func (Codec) Apply added in v0.2.0

func (Codec) Apply(path string, src []byte, edits []config.Edit) ([]byte, error)

Apply folds the edits into the document, preserving key order and — for edits to keys that already exist — every byte the edit did not touch.

A new key is inserted compactly by sjson, so when an edit adds one the document is re-indented to its own detected style, which normalises indentation but keeps key order (SortKeys is false). An edit that only changes existing keys is left byte-exact. A compact, single-line document stays compact.

config.Edit.Path is a dot-separated path whose segments are literal object keys, so each segment is escaped for sjson and gjson before it is handed to them — see [sjsonPath] and [gjsonPath]. Without that a key literally named "0" would address an array index, and a key containing "*", "?", "#", "@", "|" or "\\" would mis-address or silently no-op, because those characters carry meta-meaning in sjson/gjson path syntax.

func (Codec) Check added in v0.2.0

func (Codec) Check(path string, src []byte) error

func (Codec) Decode added in v0.2.0

func (Codec) Decode(path string, src []byte) ([]map[string]any, error)

func (Codec) Empty added in v0.2.0

func (Codec) Empty() []byte

Empty returns the content of a new, empty JSON document.

Jump to

Keyboard shortcuts

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