jsonpointer

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2025 License: MIT Imports: 5 Imported by: 7

README

JSON Pointer

Go Reference Go Report Card

Fast implementation of JSON Pointer (RFC 6901) specification in Go.

Installation

go get github.com/kaptinlin/jsonpointer

Usage

Basic Operations

Find a value in a JSON object using a JSON Pointer string:

package main

import (
    "fmt"
    "log"
    
    "github.com/kaptinlin/jsonpointer"
)

func main() {
    doc := map[string]any{
        "foo": map[string]any{
            "bar": 123,
        },
    }

    ref, err := jsonpointer.FindByPointer(doc, "/foo/bar")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println(ref.Val) // 123
}
Find by Path Components

Use variadic arguments to navigate to a value:

package main

import (
    "fmt"
    "log"
    
    "github.com/kaptinlin/jsonpointer"
)

func main() {
    doc := map[string]any{
        "foo": map[string]any{
            "bar": 123,
        },
    }

    ref, err := jsonpointer.Find(doc, "foo", "bar")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Value: %v, Object: %v, Key: %v\n", ref.Val, ref.Obj, ref.Key)
    // Value: 123, Object: map[bar:123], Key: bar
}
Safe Get Operations

Get values with error handling:

package main

import (
    "fmt"
    "log"
    
    "github.com/kaptinlin/jsonpointer"
)

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

    // Get existing value using variadic arguments (array indices as strings)
    name, err := jsonpointer.Get(doc, "users", "0", "name")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(name) // Alice

    // Get non-existing value - returns error
    missing, err := jsonpointer.Get(doc, "users", "5", "name")
    if err != nil {
        fmt.Printf("Error: %v\n", err) // Error: array index out of bounds
    } else {
        fmt.Println(missing)
    }
    
    // Get using JSON Pointer string
    age, err := jsonpointer.GetByPointer(doc, "/users/1/age")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(age) // 25
}
Path Manipulation

Convert between JSON Pointer strings and path arrays:

package main

import (
    "fmt"
    "log"
    
    "github.com/kaptinlin/jsonpointer"
)

func main() {
    // Parse JSON Pointer string to path array
    path := jsonpointer.Parse("/f~0o~1o/bar/1/baz")
    fmt.Printf("%+v\n", path)
    // [f~o/o bar 1 baz]

    // Format path components to JSON Pointer string
    pointer := jsonpointer.Format("f~o/o", "bar", "1", "baz")
    fmt.Println(pointer)
    // /f~0o~1o/bar/1/baz
    
    // Performance tip: For repeated access to the same path,
    // pre-parse the pointer once and reuse the path
    userNamePath := jsonpointer.Parse("/users/0/name")
    
    // Efficient repeated access
    for _, data := range datasets {
        name, err := jsonpointer.Get(data, userNamePath...)
        if err != nil {
            log.Printf("Error accessing user name: %v", err)
            continue
        }
        fmt.Println(name)
    }
}
Component Encoding/Decoding

Encode and decode individual path components:

package main

import (
    "fmt"
    
    "github.com/kaptinlin/jsonpointer"
)

func main() {
    // Unescape component
    unescaped := jsonpointer.Unescape("~0~1")
    fmt.Println(unescaped) // ~/

    // Escape component
    escaped := jsonpointer.Escape("~/")
    fmt.Println(escaped) // ~0~1
}
Array Operations

Working with arrays and array indices:

package main

import (
    "fmt"
    "log"
    
    "github.com/kaptinlin/jsonpointer"
)

func main() {
    doc := map[string]any{
        "items": []any{1, 2, 3},
    }

    // Access array element using variadic arguments (index as string)
    ref, err := jsonpointer.Find(doc, "items", "1")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(ref.Val) // 2

    // Array end marker "-" points to next index
    ref, err = jsonpointer.Find(doc, "items", "-")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(ref.Key) // "3" (next available index as string)
    
    // Using JSON Pointer string with Get
    value, err := jsonpointer.GetByPointer(doc, "/items/0")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(value) // 1
}
Struct Operations

Working with Go structs and JSON tags:

package main

import (
    "fmt"
    "log"
    
    "github.com/kaptinlin/jsonpointer"
)

type User struct {
    Name    string `json:"name"`
    Age     int    `json:"age"`
    Email   string // No JSON tag, uses field name
    private string // Private field, ignored
    Ignored string `json:"-"` // Explicitly ignored
}

type Profile struct {
    User     *User  `json:"user"` // Pointer to struct
    Location string `json:"location"`
}

func main() {
    profile := Profile{
        User: &User{ // Pointer to struct
            Name:    "Alice",
            Age:     30,
            Email:   "alice@example.com",
            private: "secret",
            Ignored: "ignored",
        },
        Location: "New York",
    }

    // JSON tag access using variadic arguments
    name, err := jsonpointer.Get(profile, "user", "name")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(name) // Alice

    // Field name access (no JSON tag)
    email, err := jsonpointer.Get(profile, "user", "Email")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(email) // alice@example.com

    // Private fields are ignored - returns error
    private, err := jsonpointer.Get(profile, "user", "private")
    if err != nil {
        fmt.Printf("Error: %v\n", err) // Error: not found
    }

    // json:"-" fields are ignored - returns error
    ignored, err := jsonpointer.Get(profile, "user", "Ignored")
    if err != nil {
        fmt.Printf("Error: %v\n", err) // Error: not found
    }

    // Nested struct navigation
    age, err := jsonpointer.Get(profile, "user", "age")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(age) // 30

    // JSON Pointer syntax
    ref, err := jsonpointer.FindByPointer(profile, "/user/name")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(ref.Val) // Alice

    // Mixed struct and map data
    data := map[string]any{
        "profile": profile,
        "meta":    map[string]any{"version": "1.0"},
        "users":   []User{{Name: "Bob", Age: 25}},
    }
    
    // Access struct in map
    location, err := jsonpointer.Get(data, "profile", "location")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(location) // New York
    
    // Access array of structs (index as string)
    userName, err := jsonpointer.Get(data, "users", "0", "name")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(userName) // Bob
}
Validation

Validate JSON Pointer strings:

package main

import (
    "fmt"
    
    "github.com/kaptinlin/jsonpointer"
)

func main() {
    // Valid JSON Pointer
    err := jsonpointer.Validate("/foo/bar")
    if err != nil {
        fmt.Printf("Invalid pointer: %v\n", err)
    } else {
        fmt.Println("Valid pointer")
    }

    // Invalid JSON Pointer
    err = jsonpointer.Validate("foo/bar") // missing leading slash
    if err != nil {
        fmt.Printf("Invalid pointer: %v\n", err)
    } else {
        fmt.Println("Valid pointer")
    }
}
Performance

This library offers excellent performance with zero-allocation Get operations and competitive Find operations. Our Get function achieves optimal performance for common use cases, while Find provides rich reference objects when needed.

For detailed benchmark results and performance comparisons with other JSON Pointer libraries, see benchmarks/README.md.

Acknowledgments

This project is a Go port of the excellent jsonjoy-com/json-pointer TypeScript implementation. We've adapted the core algorithms and added Go-specific performance optimizations while maintaining full RFC 6901 compatibility.

Special thanks to the original json-pointer project for providing a solid foundation and comprehensive test cases that enabled this high-quality Go implementation.

License

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

Documentation

Overview

Package jsonpointer provides JSON Pointer (RFC 6901) implementation for Go. This is a direct port of the TypeScript json-pointer library with identical behavior, using modern Go generics for type safety and performance.

This package implements helper functions for JSON Pointer (RFC 6901) specification. https://tools.ietf.org/html/rfc6901

TypeScript original source: https://github.com/jsonjoy-com/json-pointer

Usage:

import "github.com/kaptinlin/jsonpointer"

// Parse JSON Pointer string to path
path := jsonpointer.Parse("/users/0/name")

// Find value with error handling
ref, err := jsonpointer.Find(data, path...)
if err != nil {
	// Handle error
}

// Get value with error handling
value, err := jsonpointer.Get(data, path...)
if err != nil {
	// Handle error
}

// Validate JSON Pointer
err = jsonpointer.Validate("/users/0/name")

Package jsonpointer provides JSON Pointer (RFC 6901) implementation for Go. This is a direct port of the TypeScript json-pointer library with identical behavior, using modern Go generics for type safety and performance.

Index

Constants

This section is empty.

Variables

View Source
var ErrIndexOutOfBounds = errors.New("array index out of bounds")

ErrIndexOutOfBounds is returned when array index is out of bounds.

View Source
var ErrInvalidIndex = errors.New("invalid array index")

ErrInvalidIndex is returned when an invalid array index is encountered. TypeScript original code from find.ts: throw new Error('INVALID_INDEX');

View Source
var ErrInvalidPath = errors.New("invalid path")

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

View Source
var ErrInvalidPathStep = errors.New("invalid path step")

ErrInvalidPathStep is returned when a path step is not string or number. TypeScript original code from validate.ts: throw new Error('Invalid path step.');

View Source
var ErrNilPointer = errors.New("cannot traverse through nil pointer")

ErrNilPointer is returned when trying to access through nil pointer.

View Source
var ErrNoParent = errors.New("no parent")

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');

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound is returned when a path cannot be traversed. TypeScript original code from find.ts: throw new Error('NOT_FOUND');

View Source
var ErrPathTooLong = errors.New("path too long")

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.');

View Source
var ErrPointerInvalid = errors.New("pointer invalid")

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

View Source
var ErrPointerTooLong = errors.New("pointer too long")

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');

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.

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.

func IsArrayEnd

func IsArrayEnd[T any](ref ArrayReference[T]) bool

IsArrayEnd checks if an array reference points to the end of the array. TypeScript original code: export const isArrayEnd = (ref: ArrayReference): boolean => ref.obj.length === ref.key;

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 any) error

Validate validates a JSON Pointer string or Path.

func ValidatePath

func ValidatePath(path any) error

ValidatePath validates a path array.

Types

type ArrayReference

type ArrayReference[T any] struct {
	// Use pointer for undefined | T semantics (nil = undefined)
	Val *T  `json:"val"`
	Obj []T `json:"obj"`
	Key int `json:"key"` // Numeric index for array access
}

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.

func ToPath

func ToPath(pointer any) Path

ToPath converts a pointer (string or Path) to Path. If the input is a string, it parses it as JSON pointer. If the input is already a Path, it returns it as-is.

TypeScript Original: export const toPath = (pointer: string | Path) => (typeof pointer === 'string' ? parseJsonPointer(pointer) : pointer);

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.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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