hujson

package module
v0.0.0-...-fc158e5 Latest Latest
Warning

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

Go to latest
Published: May 1, 2023 License: BSD-3-Clause Imports: 11 Imported by: 0

README

HuJSON - "Human JSON" (JWCC) - with unquoted keys support

This is a fork that adds support for object keys without quotes

The github.com/tailscale/hujson package implements the JWCC extension of standard JSON.

The JWCC format permits two things over standard JSON:

  1. C-style line comments and block comments intermixed with whitespace,
  2. allows trailing commas after the last member/element in an object/array.

All JSON is valid JWCC.

For details, see the JWCC docs at:

https://nigeltao.github.io/blog/2021/json-with-commas-comments.html

Visual Studio Code association

Visual Studio Code supports a similar jsonc (JSON with comments) format. To treat all *.hujson files as jsonc with trailing commas allowed, you can add the following snippet to your Visual Studio Code configuration:

"files.associations": {
    "*.hujson": "jsonc"
},
"json.schemas": [{
    "fileMatch": ["*.hujson"],
    "schema": {
        "allowTrailingCommas": true
    }
}]

Unquoted keys

Edited to support unquoted keys like for example {position: {x: 1, y: 2}}. An unquoted key can be any valid JS variable name with ASCII characters. The syntax of an unquoted key in PCRE regex syntax would be [:alpha:][:alnum:]*.

Documentation

Overview

Package hujson contains a parser and packer for the JWCC format: JSON With Commas and Comments (or "human JSON").

JWCC is an extension of standard JSON (as defined in RFC 8259) in order to make it more suitable for humans and configuration files. In particular, it supports line comments (e.g., //...), block comments (e.g., /*...*/), and trailing commas after the last member or element in a JSON object or array.

See https://nigeltao.github.io/blog/2021/json-with-commas-comments.html

Functionality

The Parse function parses HuJSON input as a Value, which is a syntax tree exactly representing the input. Comments and whitespace are represented using the Extra type. Composite types in JSON are represented using the Object and Array types. Primitive types in JSON are represented using the Literal type. The Value.Pack method serializes the syntax tree as raw output, which is byte-for-byte identical to the input if no transformations were performed on the value.

A HuJSON value can be transformed using the Minimize, Standardize, Format, or Patch methods. Each of these methods mutate the value in place. Call the Clone method beforehand in order to preserve the original value. The Minimize and Standardize methods coerces HuJSON into standard JSON. The Format method formats the value; it is similar to `go fmt`, but instead for the HuJSON and standard JSON format. The Patch method applies a JSON Patch (RFC 6902) to the receiving value.

Grammar

The changes to the JSON grammar are:

--- grammar.json
+++ grammar.hujson
@@ -1,13 +1,31 @@
 members
 	member
+	member ',' ws
 	member ',' members

 elements
 	element
+	element ',' ws
 	element ',' elements

+comments
+	"*/"
+	comment comments
+
+comment
+	'0000' . '10FFFF'
+
+linecomments
+	'\n'
+	linecomment linecomments
+
+linecomment
+	'0000' . '10FFFF' - '\n'
+
 ws
 	""
+	"/*" comments
+	"//" linecomments
 	'0020' ws
 	'000A' ws
 	'000D' ws

Use with the Standard Library

This package operates with HuJSON as an AST. In order to parse HuJSON into arbitrary Go types, use this package to parse HuJSON input as an AST, strip the AST of any HuJSON-specific lexicographical elements, and then pack the AST as a standard JSON output.

Example usage:

b, err := hujson.Standardize(b)
if err != nil {
	... // handle err
}
if err := json.Unmarshal(b, &v); err != nil {
	... // handle err
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Format

func Format(b []byte) ([]byte, error)

Format formats b according to some opinionated heuristics for how HuJSON should look. The exact output may change over time. It is the equivalent of `go fmt` but for HuJSON.

If the input is standard JSON, then the output will remain standard. Format is idempotent such that formatting already formatted HuJSON results in no changes. If an error is encountered, then b is returned as is along with the error.

func Minimize

func Minimize(b []byte) ([]byte, error)

Minimize removes all whitespace, comments, and trailing commas from b, making it compliant with standard JSON per RFC 8259. If an error is encountered, then b is returned as is along with the error.

func Standardize

func Standardize(b []byte) ([]byte, error)

Standardize strips any features specific to HuJSON from b, making it compliant with standard JSON per RFC 8259. All comments and trailing commas are replaced with a space character in order to preserve the original line numbers and byte offsets. If an error is encountered, then b is returned as is along with the error.

Types

type Array

type Array struct {
	// Elements are the elements of a JSON array.
	// A trailing comma is emitted only if the Value.AfterExtra
	// on the last value is non-nil. Otherwise it is omitted.
	Elements []ArrayElement
	// AfterExtra are the comments and whitespace
	// after the preceding open bracket or comma and before the closing bracket.
	AfterExtra Extra
}

Array is an exact syntactic representation of a JSON array.

func (Array) Kind

func (Array) Kind() Kind

type ArrayElement

type ArrayElement = Value

type Extra

type Extra []byte

Extra is the raw bytes for whitespace and comments. Whitespace per RFC 8259, section 2 are permitted. Line comments that start with "//" and end with "\n" are permitted. Block comments that start with "/*" and end with "*/" are permitted.

func (Extra) IsStandard

func (b Extra) IsStandard() bool

IsStandard reports whether this is standard JSON whitespace.

func (Extra) IsValid

func (b Extra) IsValid() bool

IsValid reports whether the whitespace and comments are valid according to the HuJSON grammar.

type Kind

type Kind byte

Kind reports the kind of the JSON value. It is the first byte of the grammar for that JSON value, with the exception that JSON numbers are represented as a '0'.

'n': null
'f': false
't': true
'"': string
'0': number
'{': object
'[': array

type Literal

type Literal []byte // e.g., null, false, true, "string", or 3.14159

Literal is the raw bytes for a JSON null, boolean, string, or number. It contains no surrounding whitespace or comments.

func Bool

func Bool(v bool) Literal

Bool constructs a JSON literal for a boolean.

func Float

func Float(v float64) Literal

Float construct a JSON literal for a floating-point number. The values NaN, +Inf, and -Inf will be represented as a JSON string with the values "NaN", "Infinity", and "-Infinity".

func Int

func Int(v int64) Literal

Int construct a JSON literal for a signed integer.

func String

func String(v string) Literal

String constructs a JSON literal for string. Invalid UTF-8 is mangled with the Unicode replacement character.

func Uint

func Uint(v uint64) Literal

Uint construct a JSON literal for an unsigned integer.

func (Literal) Bool

func (b Literal) Bool() bool

Bool returns the value for a JSON boolean. It returns false if the literal is not a JSON boolean.

func (Literal) Float

func (b Literal) Float() (n float64)

Float returns the floating-point value for a JSON number. It returns a NaN, +Inf, or -Inf value for any JSON string with the values "NaN", "Infinity", or "-Infinity". It returns 0 for all other cases.

func (Literal) Int

func (b Literal) Int() (n int64)

Int returns the signed integer value for a JSON number. It returns 0 if the literal is not a signed integer.

func (Literal) IsValid

func (b Literal) IsValid() bool

IsValid reports whether b is a valid JSON null, boolean, string, or number. The literal must not have surrounding whitespace.

func (Literal) Kind

func (b Literal) Kind() Kind

Kind represents each possible JSON literal kind with a single byte, which is conveniently the first byte of that kind's grammar with the restriction that numbers always be represented with '0'.

func (Literal) String

func (b Literal) String() (s string)

String returns the unescaped string value for a JSON string. For other JSON kinds, this returns the raw JSON represention.

func (Literal) Uint

func (b Literal) Uint() (n uint64)

Uin returns the unsigned integer value for a JSON number. It returns 0 if the literal is not an unsigned integer.

type Object

type Object struct {
	// Members are the members of a JSON object.
	// A trailing comma is emitted only if the Value.AfterExtra
	// on the last value is non-nil. Otherwise it is omitted.
	Members []ObjectMember
	// AfterExtra are the comments and whitespace
	// after the preceding open brace or comma and before the closing brace.
	AfterExtra Extra
}

Object is an exact syntactic representation of a JSON object.

func (Object) Kind

func (Object) Kind() Kind

type ObjectMember

type ObjectMember struct {
	Name, Value Value
}

type Value

type Value struct {
	// BeforeExtra are the comments and whitespace before Value.
	// This is the extra after the preceding open brace, open bracket,
	// colon, comma, or start of input.
	BeforeExtra Extra
	// StartOffset is the offset of the first byte in Value.
	StartOffset int
	// Value is the JSON value without surrounding whitespace or comments.
	Value ValueTrimmed
	// EndOffset is the offset of the next byte after Value.
	EndOffset int
	// AfterExtra are the comments and whitespace after Value.
	// This is the extra before the succeeding colon, comma, or end of input.
	AfterExtra Extra
}

Value is an exact syntactic representation of a JSON value. The starting and ending byte offsets are populated when parsing, but are otherwise ignored when packing.

By convention, code should operate on a non-pointer Value as a soft signal that the value should not be mutated, while operating on a pointer to Value to indicate that the value may be mutated. A non-pointer Value does not provide any language-enforced guarantees that it cannot be mutated. The Value.Clone method can be used to produce a deep copy of Value such that mutations on it will not be observed in the original Value.

func Parse

func Parse(b []byte) (Value, error)

Parse parses a HuJSON value as a Value. Extra and Literal values in v will alias the provided input buffer.

func (Value) Clone

func (v Value) Clone() Value

Clone returns a deep copy of the value.

func (*Value) Find

func (v *Value) Find(ptr string) *Value

Find locates the value specified by the JSON pointer (see RFC 6901). It returns nil if the value does not exist or the pointer is invalid. If a JSON object has multiple members matching a given name, the first is returned. Object names are matched exactly, rather than with a case-insensitive match.

func (*Value) Format

func (v *Value) Format()

Format formats the value according to some opinionated heuristics for how HuJSON should look. The exact output may change over time. It is the equivalent of `go fmt` but for HuJSON.

If the input is standard JSON, then the output will remain standard. Format is idempotent such that formatting already formatted HuJSON results in no changes.

func (Value) IsStandard

func (v Value) IsStandard() bool

IsStandard reports whether this is standard JSON by checking that there are no comments and no trailing commas.

func (*Value) Minimize

func (v *Value) Minimize()

Minimize removes all whitespace, comments, and trailing commas from v, making it compliant with standard JSON per RFC 8259. Also assumes that unquoted keys are *NOT* present.

TODO: Take a standardize bool parameter

func (Value) Pack

func (v Value) Pack() []byte

Pack serializes the value as HuJSON. The output is valid so long as every Extra and Literal in the Value is valid. The output does not alias the memory of any buffers referenced by v.

func (Value) PackWithQuotedKeys

func (v Value) PackWithQuotedKeys() []byte

func (*Value) Patch

func (v *Value) Patch(patch []byte) error

Patch patches the value according to the provided patch file (per RFC 6902). The patch file may be in the HuJSON format where comments around and within a value being inserted are preserved. If the patch fails to fully apply, the receiver value will be left in a partially mutated state. Use Clone to preserve the original value.

It does not format the value. It is recommended that Format be called after applying a patch.

func (*Value) Range

func (v *Value) Range(f func(v *Value) bool) bool

Range iterates through a Value in depth-first order and calls f for each value (including the root value). It stops iteration when f returns false.

func (*Value) Standardize

func (v *Value) Standardize()

Standardize strips any features specific to HuJSON from v, making it compliant with standard JSON per RFC 8259. All comments and trailing commas are replaced with a space character in order to preserve the original line numbers and byte offsets.

func (Value) String

func (v Value) String() string

String is a string representation of v.

func (*Value) UpdateOffsets

func (v *Value) UpdateOffsets()

UpdateOffsets iterates through v and updates all Value.StartOffset and Value.EndOffset fields so that they are accurate.

type ValueTrimmed

type ValueTrimmed interface {
	// Kind reports the kind of the JSON value.
	Kind() Kind
	// contains filtered or unexported methods
}

ValueTrimmed is a JSON value without surrounding whitespace or comments. This is a sum type consisting of Literal, *Object, or *Array.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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