jsonpointer

package module
v0.4.25 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 7 Imported by: 7

README

JSON Pointer

Go Module Go Reference

A read-only JSON Pointer (RFC 6901) library for Go that traverses maps, slices, arrays, structs, and pointers with explicit errors

Features

  • RFC 6901 semantics: Parse, format, escape, unescape, and validate JSON Pointer strings with the expected token rules
  • Go-native traversal: Read map[string]any, slices, arrays, structs, and pointers without converting everything to generic JSON first
  • Explicit errors: Distinguish missing keys, missing struct fields, invalid indexes, nil pointers, and generic traversal failures
  • Small API: Learn Get, Find, GetByPointer, FindByPointer, and a handful of path helpers
  • Fast common paths: Optimize map[string]any and []any reads while keeping reflective fallbacks for typed Go values
  • Benchmarked and tested: Includes package tests, executable examples, fuzz tests, and benchmark comparisons

Installation

go get github.com/kaptinlin/jsonpointer

Requires the Go version declared in go.mod.

Quick Start

package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	doc := map[string]any{
		"users": []any{
			map[string]any{"name": "Alice"},
		},
	}

	name, err := jsonpointer.GetByPointer(doc, "/users/0/name")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(name)
}

Core APIs

API Description
Get(doc any, path ...string) (any, error) Read a value by path tokens
Find(doc any, path ...string) (*Reference, error) Read a value and return its parent container plus last key
GetByPointer(doc any, pointer string) (any, error) Read a value directly from a pointer string
FindByPointer(doc any, pointer string) (*Reference, error) Read a reference directly from a pointer string
Parse(pointer string) Path Convert a pointer string to path tokens
Format(path ...string) string Convert path tokens to a pointer string
Escape(component string) string Escape ~ and / in one token
Unescape(component string) string Reverse Escape for one token
Validate(pointer string) error Validate pointer syntax and length
ValidatePath(path Path) error Validate path length

GetByPointer and FindByPointer do not call Validate automatically. If you need strict pointer syntax checks before traversal, call Validate explicitly.

Reference Results

Find and FindByPointer return a Reference:

Field Meaning
Val The resolved value
Obj The parent container when one exists
Key The final path token used to reach Val

Use IsArrayReference and IsObjectReference when you need to inspect the returned parent context.

Examples

Read by path tokens
doc := map[string]any{
	"users": []any{
		map[string]any{"name": "Alice"},
	},
}

name, err := jsonpointer.Get(doc, "users", "0", "name")
if err != nil {
	log.Fatal(err)
}
fmt.Println(name)
Read by pointer string
doc := map[string]any{
	"foo/bar": map[string]any{
		"tilde~key": "ready",
	},
}

ref, err := jsonpointer.FindByPointer(doc, "/foo~1bar/tilde~0key")
if err != nil {
	log.Fatal(err)
}
fmt.Println(ref.Val)
fmt.Println(ref.Key)
Traverse structs and pointers
type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

user := &User{Name: "Alice", Email: "alice@example.com"}
email, err := jsonpointer.Get(user, "email")
if err != nil {
	log.Fatal(err)
}
fmt.Println(email)
Work with path utilities
path := jsonpointer.Parse("/foo~1bar/tilde~0key")
fmt.Println(path)
fmt.Println(jsonpointer.Format(path...))
fmt.Println(jsonpointer.Validate("/foo~1bar/tilde~0key") == nil)

The examples in this README are mirrored in example_test.go so go test checks they stay correct.

Error Handling

Common sentinel errors include:

  • ErrKeyNotFound
  • ErrFieldNotFound
  • ErrInvalidIndex
  • ErrIndexOutOfBounds
  • ErrNilPointer
  • ErrNotFound
  • ErrPointerInvalid
  • ErrPointerTooLong
  • ErrPathTooLong

Use errors.Is when checking traversal and validation failures.

Performance

The package optimizes common map[string]any and []any reads and falls back to reflection for typed Go values. See benchmarks/README.md for comparison data and benchmark coverage.

Run benchmarks with:

task bench

Development

task test          # Run package tests with the race detector
task lint          # Run golangci-lint and tidy checks
task yamllint      # Lint YAML files
task bench         # Run benchmarks

Run the demo program with:

go run ./examples

For development workflow and package contracts, see AGENTS.md and SPECS/.

Contributing

Contributions are welcome. Keep README.md, example_test.go, and the relevant SPECS/ documents aligned when public behavior changes.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package jsonpointer implements read-only JSON Pointer (RFC 6901) traversal helpers for Go values.

The implementation follows https://github.com/jsonjoy-com/json-pointer behavior. Public traversal APIs return errors for invalid paths and unsupported access patterns.

Index

Examples

Constants

View Source
const (
	// MaxPointerLength is the maximum allowed length for JSON Pointer strings.
	MaxPointerLength = 1024

	// MaxPathLength is the maximum allowed length for Path arrays.
	MaxPathLength = 256
)

Variables

View Source
var (
	// ErrInvalidIndex is returned when an invalid array index is encountered.
	// TypeScript original code from find.ts:
	// throw new Error('INVALID_INDEX');
	ErrInvalidIndex = errors.New("invalid array index")

	// ErrNotFound is returned when a path cannot be traversed.
	// TypeScript original code from find.ts:
	// throw new Error('NOT_FOUND');
	ErrNotFound = errors.New("not found")

	// ErrNoParent is returned when trying to get parent of root path.
	// TypeScript original code from util.ts:
	// if (path.length < 1) throw new Error('NO_PARENT');
	ErrNoParent = errors.New("no parent")

	// ErrPointerInvalid is returned when a JSON Pointer string is invalid.
	// TypeScript original code from validate.ts:
	// if (pointer[0] !== '/') throw new Error('POINTER_INVALID');
	ErrPointerInvalid = errors.New("pointer invalid")

	// ErrPointerTooLong is returned when a JSON Pointer string exceeds maximum length.
	// TypeScript original code from validate.ts:
	// if (pointer.length > 1024) throw new Error('POINTER_TOO_LONG');
	ErrPointerTooLong = errors.New("pointer too long")

	// ErrInvalidPath is returned when a path is not an array.
	// TypeScript original code from validate.ts:
	// if (!isArray(path)) throw new Error('Invalid path.');
	ErrInvalidPath = errors.New("invalid path")

	// ErrPathTooLong is returned when a path array exceeds maximum length.
	// TypeScript original code from validate.ts:
	// if (path.length > 256) throw new Error('Path too long.');
	ErrPathTooLong = errors.New("path too long")

	// ErrInvalidPathStep is returned when a path step is not string or number.
	// TypeScript original code from validate.ts:
	// throw new Error('Invalid path step.');
	ErrInvalidPathStep = errors.New("invalid path step")
)
View Source
var (
	// ErrIndexOutOfBounds is returned when array index is out of bounds.
	ErrIndexOutOfBounds = errors.New("array index out of bounds")

	// ErrNilPointer is returned when trying to access through nil pointer.
	ErrNilPointer = errors.New("cannot traverse through nil pointer")

	// ErrFieldNotFound is returned when trying to access a non-existent struct field.
	ErrFieldNotFound = errors.New("struct field not found")

	// ErrKeyNotFound is returned when trying to access a non-existent map key.
	ErrKeyNotFound = errors.New("map key not found")
)

Functions

func Escape added in v0.3.0

func Escape(component string) string

Escape escapes special characters in a path component.

func Format added in v0.3.0

func Format(path ...string) string

Format formats string path components into a JSON Pointer string.

func Get

func Get(doc any, path ...string) (any, error)

Get retrieves a value from document using string path components. Returns errors for invalid operations, similar to Find function.

Example
package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	doc := map[string]any{
		"users": []any{
			map[string]any{"name": "Alice"},
		},
	}

	name, err := jsonpointer.Get(doc, "users", "0", "name")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(name)
}
Output:
Alice
Example (Struct)
package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	type user struct {
		Name  string `json:"name"`
		Email string `json:"email"`
	}

	val, err := jsonpointer.Get(&user{Name: "Alice", Email: "alice@example.com"}, "email")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(val)
}
Output:
alice@example.com

func GetByPointer added in v0.3.0

func GetByPointer(doc any, pointer string) (any, error)

GetByPointer retrieves a value from document using JSON Pointer string. Returns errors for invalid operations.

Example
package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	doc := map[string]any{
		"users": []any{
			map[string]any{"name": "Alice"},
		},
	}

	name, err := jsonpointer.GetByPointer(doc, "/users/0/name")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(name)
}
Output:
Alice

func IsArrayReference

func IsArrayReference(ref Reference) bool

IsArrayReference checks if a Reference points to an array element. TypeScript original code: export const isArrayReference = <T = unknown>(ref: Reference): ref is ArrayReference<T> =>

isArray(ref.obj) && typeof ref.key === 'number';

func IsChild

func IsChild(parent, child Path) bool

IsChild returns true if parent contains child path, false otherwise.

TypeScript Original:

export function isChild(parent: Path, child: Path): boolean {
  if (parent.length >= child.length) return false;
  for (let i = 0; i < parent.length; i++) if (parent[i] !== child[i]) return false;
  return true;
}

func IsInteger

func IsInteger(str string) bool

IsInteger checks if a string contains only digit characters (0-9).

TypeScript Original:

export const isInteger = (str: string): boolean => {
  const len = str.length;
  let i = 0;
  let charCode: any;
  while (i < len) {
    charCode = str.charCodeAt(i);
    if (charCode >= 48 && charCode <= 57) {
      i++;
      continue;
    }
    return false;
  }
  return true;
};

func IsObjectReference

func IsObjectReference(ref Reference) bool

IsObjectReference checks if a Reference points to an object property. TypeScript original code: export const isObjectReference = <T = unknown>(ref: Reference): ref is ObjectReference<T> =>

typeof ref.obj === 'object' && typeof ref.key === 'string';

func IsPathEqual

func IsPathEqual(p1, p2 Path) bool

IsPathEqual returns true if two paths are equal, false otherwise.

TypeScript Original:

export function isPathEqual(p1: Path, p2: Path): boolean {
  if (p1.length !== p2.length) return false;
  for (let i = 0; i < p1.length; i++) if (p1[i] !== p2[i]) return false;
  return true;
}

func IsRoot

func IsRoot(path Path) bool

IsRoot returns true if JSON Pointer points to root value, false otherwise.

TypeScript Original: export const isRoot = (path: Path): boolean => !path.length;

func IsValidIndex

func IsValidIndex(index string) bool

IsValidIndex checks if path component can be a valid array index.

TypeScript Original:

export function isValidIndex(index: string | number): boolean {
  if (typeof index === 'number') return true;
  const n = Number.parseInt(index, 10);
  return String(n) === index && n >= 0;
}

func Unescape added in v0.3.0

func Unescape(component string) string

Unescape unescapes special characters in a path component.

func Validate added in v0.3.0

func Validate(pointer string) error

Validate validates a JSON Pointer string.

Example
package main

import (
	"fmt"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	fmt.Println(jsonpointer.Validate("/users/0/name") == nil)
	fmt.Println(jsonpointer.Validate("users/0/name") == nil)
}
Output:
true
false

func ValidatePath

func ValidatePath(path Path) error

ValidatePath validates a path array.

Types

type ArrayReference

type ArrayReference[T any] struct {
	Val *T  `json:"val"`
	Obj []T `json:"obj"`
	Key int `json:"key"`
}

ArrayReference represents a reference to an array element. TypeScript original code:

export interface ArrayReference<T = unknown> {
  readonly val: undefined | T;
  readonly obj: T[];
  readonly key: number;
}

type ObjectReference

type ObjectReference[T any] struct {
	Val T            `json:"val"`
	Obj map[string]T `json:"obj"`
	Key string       `json:"key"`
}

ObjectReference represents a reference to an object property. TypeScript original code:

export interface ObjectReference<T = unknown> {
  readonly val: T;
  readonly obj: Record<string, T>;
  readonly key: string;
}

type Path

type Path []string

Path represents a JSON Pointer path as array of string tokens.

func Parent

func Parent(path Path) (Path, error)

Parent returns parent path, e.g. for []string{"foo", "bar", "baz"} returns []string{"foo", "bar"}. Returns ErrNoParent if the path has no parent (empty or root path).

TypeScript Original:

export function parent(path: Path): Path {
  if (path.length < 1) throw new Error('NO_PARENT');
  return path.slice(0, path.length - 1);
}

func Parse added in v0.3.0

func Parse(pointer string) Path

Parse parses a JSON Pointer string to a path array.

Example
package main

import (
	"fmt"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	path := jsonpointer.Parse("/foo~1bar/tilde~0key")
	fmt.Println(path)
	fmt.Println(jsonpointer.Format(path...))
}
Output:
[foo/bar tilde~key]
/foo~1bar/tilde~0key

type Reference

type Reference struct {
	Val any    `json:"val"`
	Obj any    `json:"obj,omitempty"`
	Key string `json:"key,omitempty"`
}

Reference represents a found reference with context.

func Find

func Find(doc any, path ...string) (*Reference, error)

Find locates a reference in document using string path components. Returns errors for invalid operations.

func FindByPointer

func FindByPointer(doc any, pointer string) (*Reference, error)

FindByPointer locates a reference in document using JSON Pointer string.

Example
package main

import (
	"fmt"
	"log"

	"github.com/kaptinlin/jsonpointer"
)

func main() {
	doc := map[string]any{
		"foo/bar": map[string]any{
			"tilde~key": "ready",
		},
	}

	ref, err := jsonpointer.FindByPointer(doc, "/foo~1bar/tilde~0key")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(ref.Val)
	fmt.Println(ref.Key)
}
Output:
ready
tilde~key

Directories

Path Synopsis
Package main demonstrates the jsonpointer API.
Package main demonstrates the jsonpointer API.

Jump to

Keyboard shortcuts

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