jsonpatch

package module
v0.7.9 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 10 Imported by: 0

README

JSON Patch Go

Go Reference Go Report Card

A Go-native JSON Patch+ library with compiled patches, type-preserving results, and immutable-by-default application

Features

  • Compiled patch path: Validate, capability-check, and freeze operations once, then reuse an isolated Patch.
  • Type-preserving results: Apply converts patched documents back to the caller's original Go type when safe.
  • Immutable by default: Apply clones before execution; ApplyInPlace makes mutation explicit.
  • Capability-gated vocabulary: RFC 6902 is the default; predicates, regex predicates, and extended operations opt in at compile time.
  • Explicit document shapes: Plain string is scalar text; use JSONText or []byte for JSON text.
  • Structured errors: Match stable failure classes with errors.Is and inspect operation context with errors.As.
  • Wire-format codecs: Use JSON, compact array, or MessagePack codecs without moving execution semantics out of operations.

Installation

go get github.com/kaptinlin/jsonpatch

Requires Go 1.26.5+.

Quick Start

Use this path when you have Go-built operations and want an immutable result while preserving the original document.

package main

import (
    "fmt"
    "log"

    "github.com/kaptinlin/jsonpatch"
    "github.com/kaptinlin/jsonpatch/op"
)

func main() {
    doc := map[string]any{"name": "John", "tags": []any{"go"}}

    patch, err := jsonpatch.Compile(
        op.NewTest([]string{"name"}, "John"),
        op.NewReplace([]string{"name"}, "Jane"),
        op.NewAdd([]string{"email"}, "jane@example.com"),
    )
    if err != nil {
        log.Fatal(err)
    }

    result, err := jsonpatch.Apply(patch, doc)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(result.Doc["name"]) // Jane
    fmt.Println(doc["name"])        // John
}

Core API

API Use when
Compile You have Go-built operations from op/ and want the default RFC 6902 vocabulary.
CompileOps You have an operation slice or need compile options.
CompileOperations You have JSON-shaped codec/json.Operation values.
CompileJSON You have a JSON patch document as bytes.
Apply You want immutable, type-preserving patch application.
ApplyInPlace You intentionally want to write the patched result back to the input variable.
JSONText You want a string document parsed as JSON text.

Capabilities

Default compilation accepts only RFC 6902 operations. Enable additional operation families explicitly.

patch, err := jsonpatch.CompileJSON(data,
    jsonpatch.WithCapabilities(
        jsonpatch.RFC6902,
        jsonpatch.Predicate,
        jsonpatch.RegexPredicate,
        jsonpatch.Extended,
    ),
)
if err != nil {
    return err
}

Use jsonpatch.AllCapabilities when your boundary intentionally accepts every operation implemented by the package.

Document Shapes

Input Processing model Output
map[string]any Apply directly map[string]any
[]byte Decode JSON, apply, encode JSON []byte
JSONText Decode JSON, apply, encode JSON JSONText
string Treat as scalar text string
Structs and concrete types Marshal to JSON, apply, unmarshal back Original Go type
Primitives and []any Apply directly when assignable Original Go type
patch, err := jsonpatch.CompileJSON([]byte(`[{"op":"replace","path":"/name","value":"Jane"}]`))
if err != nil {
    return err
}

result, err := jsonpatch.Apply(patch, jsonpatch.JSONText(`{"name":"John"}`))
if err != nil {
    return err
}

fmt.Println(result.Doc)

In-Place Application

Use ApplyInPlace when mutation is intentional and visible at the call site.

doc := map[string]any{"name": "John"}
patch, err := jsonpatch.Compile(op.NewReplace([]string{"name"}, "Jane"))
if err != nil {
    return err
}

if err := jsonpatch.ApplyInPlace(patch, &doc); err != nil {
    return err
}

fmt.Println(doc["name"])

Structured Errors

Compile and apply failures wrap stable sentinel errors and expose operation context. A failed apply returns a nil result; inspect *jsonpatch.Error for the failing operation index and fields.

result, err := jsonpatch.Apply(patch, doc)
if err != nil {
    if errors.Is(err, jsonpatch.ErrTestFailed) {
        return err
    }

    var patchErr *jsonpatch.Error
    if errors.As(err, &patchErr) {
        fmt.Printf("operation %d %s %s failed: %v\n",
            patchErr.Index(),
            patchErr.Op(),
            patchErr.Path(),
            patchErr.Cause(),
        )
    }
    return err
}

_ = result.Doc

Codecs

Codec packages translate wire formats. Operation behavior stays in op/.

JSON Codec

Use codec/json when you want to encode or decode JSON-shaped operation values directly.

operations := []jsoncodec.Operation{
    {Op: "replace", Path: "/name", Value: "Jane"},
}

patch, err := jsonpatch.CompileOperations(operations)
if err != nil {
    return err
}
Compact Codec

Use codec/compact for compact array-form operations with segment-array paths.

ops := []jsonpatch.Op{
    op.NewAdd([]string{"name"}, "Jane"),
    op.NewInc([]string{"version"}, 1),
}

encoded, err := compact.EncodeJSON(ops)
if err != nil {
    return err
}

decoded, err := compact.DecodeJSON(encoded)
if err != nil {
    return err
}

fmt.Println(len(decoded))
Binary Codec

Use codec/binary for MessagePack encoding. Decode accepts exactly one complete patch value and rejects malformed operation frame lengths, trailing bytes, and concatenated values.

codec := binary.New()
ops := []jsonpatch.Op{
    op.NewAdd([]string{"name"}, "Jane"),
    op.NewInc([]string{"version"}, 1),
}

data, err := codec.Encode(ops)
if err != nil {
    return err
}

decoded, err := codec.Decode(data)
if err != nil {
    return err
}

fmt.Println(len(decoded))

Examples

Explore runnable examples in examples/:

Development

task test           # Run all tests with race detection
task golangci-lint  # Run golangci-lint
task lint           # Run golangci-lint and tidy checks
task vet            # Run go vet
task bench          # Run benchmarks
task verify         # Run the complete release gate

For development guidelines, see AGENTS.md. For recorded behavior contracts, see SPECS/.

Contributing

See CONTRIBUTING.md.

License

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

Documentation

Overview

Package jsonpatch compiles and applies JSON Patch, predicate, and extended operations to Go values while preserving the caller's document type.

Example
package main

import (
	"fmt"

	jsoncodec "github.com/kaptinlin/jsonpatch/codec/json"

	"github.com/go-json-experiment/json"
	"github.com/go-json-experiment/json/jsontext"

	"github.com/kaptinlin/jsonpatch"
)

func main() {
	// Original document
	doc := map[string]any{
		"user": map[string]any{
			"name":  "Alice",
			"email": "alice@example.com",
			"age":   25,
		},
		"settings": map[string]any{
			"theme": "dark",
		},
	}

	// Create patch operations
	patch := []jsoncodec.Operation{
		// Add a new field
		{
			Op:    "add",
			Path:  "/user/active",
			Value: true,
		},
		// Update existing field
		{
			Op:    "replace",
			Path:  "/user/age",
			Value: 26,
		},
		// Add to settings
		{
			Op:    "add",
			Path:  "/settings/notifications",
			Value: true,
		},
	}

	compiled, err := jsonpatch.CompileOperations(patch)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	result, err := jsonpatch.Apply(compiled, doc)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	// Print result
	resultJSON, _ := json.Marshal(result.Doc, jsontext.Multiline(true), json.Deterministic(true))
	fmt.Println(string(resultJSON))

}
Output:
{
	"settings": {
		"notifications": true,
		"theme": "dark"
	},
	"user": {
		"active": true,
		"age": 26,
		"email": "alice@example.com",
		"name": "Alice"
	}
}

Index

Examples

Constants

View Source
const (
	// JSON Patch (RFC 6902) operations
	OpAddType     = internal.OpAddType
	OpRemoveType  = internal.OpRemoveType
	OpReplaceType = internal.OpReplaceType
	OpMoveType    = internal.OpMoveType
	OpCopyType    = internal.OpCopyType
	OpTestType    = internal.OpTestType

	// JSON Predicate operations
	OpContainsType      = internal.OpContainsType
	OpDefinedType       = internal.OpDefinedType
	OpUndefinedType     = internal.OpUndefinedType
	OpTypeType          = internal.OpTypeType
	OpTestTypeType      = internal.OpTestTypeType
	OpTestStringType    = internal.OpTestStringType
	OpTestStringLenType = internal.OpTestStringLenType
	OpEndsType          = internal.OpEndsType
	OpStartsType        = internal.OpStartsType
	OpInType            = internal.OpInType
	OpLessType          = internal.OpLessType
	OpMoreType          = internal.OpMoreType
	OpMatchesType       = internal.OpMatchesType

	// Composite operations
	OpAndType = internal.OpAndType
	OpOrType  = internal.OpOrType
	OpNotType = internal.OpNotType

	// Extended operations
	OpFlipType   = internal.OpFlipType
	OpIncType    = internal.OpIncType
	OpStrInsType = internal.OpStrInsType
	OpStrDelType = internal.OpStrDelType
	OpSplitType  = internal.OpSplitType
	OpMergeType  = internal.OpMergeType
	OpExtendType = internal.OpExtendType
)

These constants name the supported operations.

View Source
const AllCapabilities = RFC6902 | Predicate | RegexPredicate | Extended

AllCapabilities enables every operation vocabulary implemented by the package.

Variables

View Source
var (
	// ErrPayloadInvalid reports an invalid patch payload or compiled operation.
	ErrPayloadInvalid = errors.New("payload invalid")
	// ErrUnsupportedCapability reports an operation outside the enabled vocabulary.
	ErrUnsupportedCapability = errors.New("unsupported capability")
	// ErrRuntimeConflict reports a valid operation that cannot apply to the document state.
	ErrRuntimeConflict = errors.New("runtime conflict")
	// ErrTestFailed reports a failed predicate or test operation.
	ErrTestFailed = errors.New("test failed")
	// ErrTypeMismatch reports a runtime type mismatch.
	ErrTypeMismatch = errors.New("type mismatch")
	// ErrConversionFailed reports that a patched result could not be converted back.
	ErrConversionFailed = errors.New("failed to convert result back to original type")
)

Functions

func ApplyInPlace added in v0.7.8

func ApplyInPlace[T internal.Document](patch *Patch, doc *T) error

ApplyInPlace applies patch and stores the result back in doc.

Types

type Capability added in v0.7.8

type Capability uint64

Capability identifies an operation vocabulary that may be compiled.

const (
	// RFC6902 enables the core JSON Patch operations.
	RFC6902 Capability = 1 << iota
	// Predicate enables non-regex predicate operations.
	Predicate
	// RegexPredicate enables the matches predicate operation.
	RegexPredicate
	// Extended enables JSON Patch Extended operations.
	Extended
)

type CompileOption added in v0.7.8

type CompileOption func(*compileOptions)

CompileOption configures patch compilation.

func WithCapabilities added in v0.7.8

func WithCapabilities(capabilities ...Capability) CompileOption

WithCapabilities sets the operation vocabularies accepted during compilation.

func WithCompileMatcher added in v0.7.8

func WithCompileMatcher(createMatcher CreateRegexMatcher) CompileOption

WithCompileMatcher sets the regex matcher factory used while decoding matches operations.

type CreateRegexMatcher

type CreateRegexMatcher = internal.CreateRegexMatcher

CreateRegexMatcher creates a RegexMatcher from a pattern.

type Document added in v0.2.0

type Document = internal.Document

Document is the set of document types supported by the generic API.

type Error added in v0.7.8

type Error struct {
	// contains filtered or unexported fields
}

Error carries stable patch failure context for programmatic inspection.

func (*Error) Cause added in v0.7.8

func (e *Error) Cause() error

Cause returns the original wrapped error.

func (*Error) Codec added in v0.7.8

func (e *Error) Codec() string

Codec returns the codec boundary associated with the failure.

func (*Error) Error added in v0.7.8

func (e *Error) Error() string

Error returns a human-readable failure description.

func (*Error) From added in v0.7.8

func (e *Error) From() string

From returns the failing operation source path when present.

func (*Error) Index added in v0.7.8

func (e *Error) Index() int

Index returns the failing operation index, or -1 when no operation was decoded.

func (*Error) Kind added in v0.7.8

func (e *Error) Kind() error

Kind returns the stable failure class.

func (*Error) Op added in v0.7.8

func (e *Error) Op() string

Op returns the failing operation name.

func (*Error) Path added in v0.7.8

func (e *Error) Path() string

Path returns the failing operation target path.

func (*Error) Unwrap added in v0.7.8

func (e *Error) Unwrap() error

Unwrap exposes both the stable kind and original cause to errors.Is/As.

type JSONText added in v0.7.8

type JSONText string

JSONText marks a string as JSON text instead of a scalar string document.

type Op

type Op = internal.Op

Op applies itself to a document.

type OpType

type OpType = internal.OpType

OpType names a JSON Patch operation.

type Patch added in v0.7.8

type Patch struct {
	// contains filtered or unexported fields
}

Patch is a compiled, reusable operation sequence.

func Compile added in v0.7.8

func Compile(ops ...Op) (*Patch, error)

Compile compiles Go-built operations with the default RFC 6902 capability.

func CompileJSON added in v0.7.8

func CompileJSON(data []byte, opts ...CompileOption) (*Patch, error)

CompileJSON compiles a JSON patch document.

func CompileOperations added in v0.7.8

func CompileOperations(operations []jsoncodec.Operation, opts ...CompileOption) (*Patch, error)

CompileOperations compiles JSON-shaped Operation values.

func CompileOps added in v0.7.8

func CompileOps(ops []Op, opts ...CompileOption) (*Patch, error)

CompileOps compiles Go-built operations.

func (*Patch) Len added in v0.7.8

func (p *Patch) Len() int

Len returns the number of compiled operations.

type RegexMatcher

type RegexMatcher = internal.RegexMatcher

RegexMatcher tests if a value matches a pattern.

func CreateMatcherDefault

func CreateMatcherDefault(pattern string, ignoreCase bool) (RegexMatcher, error)

CreateMatcherDefault creates a regex matcher from a pattern and case sensitivity flag. It returns a regexp syntax error when pattern is invalid.

type Result added in v0.7.8

type Result[T internal.Document] struct {
	Doc   T
	Steps []Step
}

Result is the typed result of applying a compiled patch.

func Apply added in v0.7.8

func Apply[T internal.Document](patch *Patch, doc T) (*Result[T], error)

Apply applies patch immutably to doc.

type Step added in v0.7.8

type Step struct {
	// contains filtered or unexported fields
}

Step describes one applied operation.

func (*Step) From added in v0.7.8

func (s *Step) From() string

From returns the operation source path when present.

func (*Step) Index added in v0.7.8

func (s *Step) Index() int

Index returns the operation index.

func (*Step) Old added in v0.7.8

func (s *Step) Old() any

Old returns the previous value reported by the operation.

func (*Step) Op added in v0.7.8

func (s *Step) Op() string

Op returns the operation name.

func (*Step) Path added in v0.7.8

func (s *Step) Path() string

Path returns the operation target path.

Directories

Path Synopsis
codec
binary
Package binary implements a MessagePack-based binary codec for JSON Patch operations.
Package binary implements a MessagePack-based binary codec for JSON Patch operations.
compact
Package compact implements an array-based codec for JSON Patch operations.
Package compact implements an array-based codec for JSON Patch operations.
json
Package json implements a codec for JSON Patch operations with full RFC 6902, JSON Predicate, and extended operation support.
Package json implements a codec for JSON Patch operations with full RFC 6902, JSON Predicate, and extended operation support.
json/tests
Package tests provides test data and automated codec tests for the JSON codec.
Package tests provides test data and automated codec tests for the JSON codec.
examples
apply-in-place command
Package main demonstrates explicit in-place JSON Patch application.
Package main demonstrates explicit in-place JSON Patch application.
array-operations command
Package main demonstrates array operations using JSON Patch.
Package main demonstrates array operations using JSON Patch.
basic command
Package main demonstrates basic JSON Patch operations.
Package main demonstrates basic JSON Patch operations.
basic-operations command
Package main demonstrates basic JSON Patch operations.
Package main demonstrates basic JSON Patch operations.
batch-update command
Package main demonstrates batch update operations using JSON Patch.
Package main demonstrates batch update operations using JSON Patch.
binary-codec command
Package main demonstrates binary codec operations using JSON Patch.
Package main demonstrates binary codec operations using JSON Patch.
compact-codec command
Package main demonstrates the compact codec functionality.
Package main demonstrates the compact codec functionality.
conditional-operations command
Package main demonstrates conditional operations using JSON Patch.
Package main demonstrates conditional operations using JSON Patch.
copy-move-operations command
Package main demonstrates copy and move operations using JSON Patch.
Package main demonstrates copy and move operations using JSON Patch.
error-handling command
Package main demonstrates error handling with JSON Patch operations.
Package main demonstrates error handling with JSON Patch operations.
errors command
Package main demonstrates error handling in JSON Patch operations.
Package main demonstrates error handling in JSON Patch operations.
extended command
Package main demonstrates extended JSON Patch operations.
Package main demonstrates extended JSON Patch operations.
json-bytes-patch command
Package main demonstrates JSON bytes patching operations.
Package main demonstrates JSON bytes patching operations.
json-string-patch command
Package main demonstrates JSON string patching operations.
Package main demonstrates JSON string patching operations.
map-patch command
Package main demonstrates map patching operations using JSON Patch.
Package main demonstrates map patching operations using JSON Patch.
string-operations command
Package main demonstrates string operations using JSON Patch.
Package main demonstrates string operations using JSON Patch.
struct-patch command
Package main demonstrates struct patching operations using JSON Patch.
Package main demonstrates struct patching operations using JSON Patch.
types command
Package main demonstrates type operations with JSON Patch.
Package main demonstrates type operations with JSON Patch.
Package internal provides shared types, interfaces, and constants for JSON Patch operations.
Package internal provides shared types, interfaces, and constants for JSON Patch operations.
Package op provides implementations for JSON Patch operations.
Package op provides implementations for JSON Patch operations.
tests
data
Package data contains test case data and specifications for JSON Patch operations.
Package data contains test case data and specifications for JSON Patch operations.
testutils
Package testutils provides shared utilities for JSON Patch testing.
Package testutils provides shared utilities for JSON Patch testing.

Jump to

Keyboard shortcuts

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