jsonpatch

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2025 License: MIT Imports: 10 Imported by: 0

README

JSON Patch Go

A comprehensive Go implementation of JSON Patch (RFC 6902), JSON Predicate, and extended operations for JSON document manipulation with full type safety and generic support.

Note: This is a Golang port of the powerful json-joy/json-patch, bringing JSON Patch extended operations to the Go ecosystem with modern Go generics.

Go Reference Go Report Card

🚀 Quick Start

Installation
go get github.com/kaptinlin/jsonpatch
Basic Usage
package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/kaptinlin/jsonpatch"
)

func main() {
    // Original document
    doc := map[string]interface{}{
        "name": "John",
        "age":  30,
    }

    // Create patch operations
    patch := []jsonpatch.Operation{
        {
            "op":    "replace",
            "path":  "/name",
            "value": "Jane",
        },
        {
            "op":    "add",
            "path":  "/email",
            "value": "jane@example.com",
        },
    }

    // Apply patch with type-safe generic API
    result, err := jsonpatch.ApplyPatch(doc, patch)
    if err != nil {
        log.Fatalf("Failed to apply patch: %v", err)
    }

    // result.Doc is automatically typed as map[string]interface{}
    // No type assertions needed!
    fmt.Printf("Name: %s\n", result.Doc["name"])
    fmt.Printf("Email: %s\n", result.Doc["email"])

    // Output result as JSON
    output, _ := json.MarshalIndent(result.Doc, "", "  ")
    fmt.Println(string(output))
    // Output:
    // {
    //   "age": 30,
    //   "email": "jane@example.com",
    //   "name": "Jane"
    // }
}
Type-Safe Generic Usage
// Define your own types for complete type safety
type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Age   int    `json:"age"`
}

func main() {
    user := User{Name: "John", Age: 30}
    
    patch := []jsonpatch.Operation{
        {"op": "replace", "path": "/name", "value": "Jane"},
        {"op": "add", "path": "/email", "value": "jane@example.com"},
    }

    // Type-safe: result.Doc is automatically typed as User
    result, err := jsonpatch.ApplyPatch(user, patch)
    if err != nil {
        log.Fatal(err)
    }

    // No type assertions needed - compile-time type safety!
    fmt.Printf("Updated user: %+v\n", result.Doc)
    // Output: Updated user: {Name:Jane Email:jane@example.com Age:30}
}
Compact Codec Usage
import "github.com/kaptinlin/jsonpatch/codec/compact"

func main() {
    // Standard JSON Patch operations
    standardOps := []jsonpatch.Operation{
        {"op": "add", "path": "/name", "value": "John"},
        {"op": "replace", "path": "/age", "value": 30},
        {"op": "remove", "path": "/temp"},
    }

    // Encode to compact format with numeric opcodes (35.9% space savings)
    encoder := compact.NewEncoder(compact.WithNumericOpcodes(true))
    compactData, err := encoder.Encode(standardOps)
    if err != nil {
        log.Fatal(err)
    }

    // Compact format: [[0,"/name","John"],[2,"/age",30],[1,"/temp"]]
    // vs Standard: [{"op":"add","path":"/name","value":"John"},...]

    // Decode back to standard operations
    decoder := compact.NewDecoder()
    decodedOps, err := decoder.Decode(compactData)
    if err != nil {
        log.Fatal(err)
    }

    // Perfect round-trip compatibility
    fmt.Printf("Original ops count: %d\n", len(standardOps))
    fmt.Printf("Decoded ops count: %d\n", len(decodedOps))
    // Output: Original ops count: 3
    //         Decoded ops count: 3
}

🎯 Features

Type-Safe Generic API
  • Full Generic Support - No more interface{} or type assertions
  • Compile-Time Type Safety - Catch type errors at compile time
  • Automatic Type Inference - Result types are automatically inferred
  • Zero-Value Usability - Works without configuration
✅ RFC 6902 Standard Operations (docs)
  • add - Add new values to the document
  • remove - Remove existing values
  • replace - Replace existing values
  • move - Move values to different locations
  • copy - Copy values to new locations
  • test - Test values for conditional operations
🔍 JSON Predicate Operations (docs)
  • contains - Check if strings contain substrings
  • defined - Test if paths exist
  • ends - Test string endings
  • in - Check membership in arrays
  • matches - Regular expression matching
  • starts - Test string beginnings
  • type - Type validation
  • less/more - Numeric comparisons
🔧 Extended Operations (docs)
  • str_ins - String insertion for text editing
  • str_del - String deletion
  • inc - Increment numeric values
  • flip - Boolean toggling
  • split - Object splitting
  • merge - Object merging
🗜️ Compact Codec (codec/compact/)
  • Space Efficient - 35.9% size reduction with numeric opcodes
  • Array Format - [opcode, path, ...args] instead of verbose objects
  • Both Formats - Support for numeric (0,1,2...) and string ("add","remove"...) opcodes
  • Round-trip Compatible - Perfect conversion between standard and compact formats
  • High Performance - O(1) opcode lookups and optimized encoding/decoding

📖 Examples

Explore comprehensive examples in the examples/ directory:

Core Operations
Document Types
Codecs
Advanced
# Run any example
cd examples/<example-name> && go run main.go

# Try the compact codec demo
cd examples/compact-codec && go run main.go

📚 API Reference

Core Generic Functions
// Apply a single operation with type safety
func ApplyOp[T Document](doc T, operation Op, opts ...Option) (*OpResult[T], error)

// Apply multiple operations with type safety
func ApplyOps[T Document](doc T, operations []Op, opts ...Option) (*PatchResult[T], error)

// Apply JSON Patch with type safety
func ApplyPatch[T Document](doc T, patch []Operation, opts ...Option) (*PatchResult[T], error)
Compact Codec Functions
import "github.com/kaptinlin/jsonpatch/codec/compact"

// Create encoder with options
func NewEncoder(opts ...EncoderOption) *Encoder
func WithNumericOpcodes(numeric bool) EncoderOption

// Create decoder with options  
func NewDecoder(opts ...DecoderOption) *Decoder

// Encode operations to compact format
func (e *Encoder) Encode(ops []jsonpatch.Operation) ([]byte, error)

// Decode from compact format to operations
func (d *Decoder) Decode(data []byte) ([]jsonpatch.Operation, error)

// Convenience functions
func Encode(ops []jsonpatch.Operation, opts ...EncoderOption) ([]byte, error)
func Decode(data []byte, opts ...DecoderOption) ([]jsonpatch.Operation, error)
Functional Options
// Configure mutation behavior
func WithMutate(mutate bool) Option

// Configure custom regex matcher
func WithMatcher(createMatcher func(string, bool) RegexMatcher) Option
Result Types
// Generic result for single operation
type OpResult[T Document] struct {
    Doc T    // Result document with preserved type
    Old any  // Previous value at the path
}

// Generic result for multiple operations
type PatchResult[T Document] struct {
    Doc T                // Result document with preserved type
    Res []OpResult[any]  // Results for each operation
}

🔨 Common Patterns

1. Type-Safe Operations
type Config struct {
    Version int    `json:"version"`
    Status  string `json:"status"`
    Enabled bool   `json:"enabled"`
}

config := Config{Version: 1, Status: "active", Enabled: true}

patch := []jsonpatch.Operation{
    {"op": "inc", "path": "/version", "inc": 1},
    {"op": "replace", "path": "/status", "value": "updated"},
    {"op": "flip", "path": "/enabled"},
}

// result.Doc is automatically typed as Config
result, err := jsonpatch.ApplyPatch(config, patch)
if err != nil {
    log.Fatal(err)
}

// No type assertions needed!
fmt.Printf("Version: %d, Status: %s, Enabled: %t\n", 
    result.Doc.Version, result.Doc.Status, result.Doc.Enabled)
2. Safe Updates with Test Operations
patch := []jsonpatch.Operation{
    // Test current value before modifying
    {
        "op":    "test",
        "path":  "/version",
        "value": 1,
    },
    // Make the change
    {
        "op":    "replace",
        "path":  "/status",
        "value": "updated",
    },
    // Increment version
    {
        "op":   "inc",
        "path": "/version",
        "inc":  1,
    },
}

result, err := jsonpatch.ApplyPatch(doc, patch)
3. Mutation Control
// Preserve original document (default)
result, err := jsonpatch.ApplyPatch(doc, patch)

// Mutate original document for performance
result, err := jsonpatch.ApplyPatch(doc, patch, jsonpatch.WithMutate(true))
4. Batch Operations
var patch []jsonpatch.Operation

// Update multiple items efficiently
for i := 0; i < itemCount; i++ {
    patch = append(patch, jsonpatch.Operation{
        "op":    "replace",
        "path":  fmt.Sprintf("/items/%d/status", i),
        "value": "processed",
    })
}

result, err := jsonpatch.ApplyPatch(doc, patch)
5. Array Manipulation
patch := []jsonpatch.Operation{
    // Add to end of array
    {
        "op":   "add",
        "path": "/users/-",
        "value": map[string]interface{}{"name": "New User"},
    },
    // Insert at specific position
    {
        "op":   "add",
        "path": "/tags/0",
        "value": "important",
    },
    // Remove array element
    {
        "op":   "remove",
        "path": "/items/2",
    },
}

result, err := jsonpatch.ApplyPatch(doc, patch)
6. String Operations
patch := []jsonpatch.Operation{
    // Insert text at position
    {
        "op":   "str_ins",
        "path": "/content",
        "pos":  0,
        "str":  "Prefix: ",
    },
    // Insert at end
    {
        "op":   "str_ins",
        "path": "/content",
        "pos":  20,
        "str":  " (Updated)",
    },
}

result, err := jsonpatch.ApplyPatch(doc, patch)
8. Error Handling
result, err := jsonpatch.ApplyPatch(doc, patch)
if err != nil {
    switch {
    case strings.Contains(err.Error(), "path not found"):
        log.Printf("Invalid path in patch: %v", err)
    case strings.Contains(err.Error(), "test operation failed"):
        log.Printf("Condition not met: %v", err)
    default:
        log.Printf("Patch application failed: %v", err)
    }
    return err
}

// Type-safe access to result
processedDoc := result.Doc
9. Compact Codec Usage
import "github.com/kaptinlin/jsonpatch/codec/compact"

// Standard operations
ops := []jsonpatch.Operation{
    {"op": "add", "path": "/users/-", "value": map[string]string{"name": "Alice"}},
    {"op": "inc", "path": "/counter", "inc": 1},
    {"op": "flip", "path": "/enabled"},
}

// Choose encoding format
numericEncoder := compact.NewEncoder(compact.WithNumericOpcodes(true))
stringEncoder := compact.NewEncoder(compact.WithNumericOpcodes(false))

// Encode (numeric opcodes for maximum space savings)
compactData, err := numericEncoder.Encode(ops)
if err != nil {
    log.Fatal(err)
}

// Decode
decoder := compact.NewDecoder()
decoded, err := decoder.Decode(compactData)
if err != nil {
    log.Fatal(err)
}

// Perfect round-trip compatibility
fmt.Printf("Space savings: %d%%\n", (len(standardJSON)-len(compactData))*100/len(standardJSON))
// Output: Space savings: 35%

📈 Best Practices

1. Leverage Type Safety
// Define specific types for your data
type UserProfile struct {
    ID       string   `json:"id"`
    Name     string   `json:"name"`
    Email    string   `json:"email"`
    Tags     []string `json:"tags"`
    Settings map[string]interface{} `json:"settings"`
}

// Get compile-time type safety
result, err := jsonpatch.ApplyPatch(userProfile, patch)
// result.Doc is automatically typed as UserProfile
2. Use Functional Options
// Default behavior (no mutation)
result, err := jsonpatch.ApplyPatch(doc, patch)

// Custom configuration
result, err := jsonpatch.ApplyPatch(doc, patch, 
    jsonpatch.WithMutate(true),
    jsonpatch.WithMatcher(customRegexMatcher),
)
3. Always Use Test Operations for Critical Updates
// Good: Test before critical changes
patch := []jsonpatch.Operation{
    {"op": "test", "path": "/balance", "value": 1000.0},
    {"op": "replace", "path": "/balance", "value": 500.0},
}
4. Choose Mutate Mode Carefully
// Use mutate: false for concurrent access (default)
// Use mutate: true for large patches when performance matters
result, err := jsonpatch.ApplyPatch(doc, patch, 
    jsonpatch.WithMutate(len(patch) > 100), // Performance optimization
)
5. Handle Errors Gracefully
if result, err := jsonpatch.ApplyPatch(doc, patch); err != nil {
    // Log error with context
    log.Printf("Patch failed for document %s: %v", docID, err)
    return originalDoc, err
}
6. Use Compact Codec for Storage/Network Efficiency
import "github.com/kaptinlin/jsonpatch/codec/compact"

// For maximum space savings (35.9% reduction)
compactData, err := compact.Encode(patch, compact.WithNumericOpcodes(true))
if err == nil {
    // Store or transmit compactData instead of standard JSON
    saveToDatabase(compactData) // 35.9% smaller
    sendOverNetwork(compactData) // 35.9% less bandwidth
}
7. Use Batch Operations for Multiple Changes
// Efficient: Single patch with multiple operations
patch := []jsonpatch.Operation{
    {"op": "replace", "path": "/status", "value": "active"},
    {"op": "inc", "path": "/version", "inc": 1},
    {"op": "add", "path": "/lastModified", "value": time.Now()},
}
10. Optimize with Compact Codec for Storage/Network
import "github.com/kaptinlin/jsonpatch/codec/compact"

// For storage or network transmission
func storeOperations(ops []jsonpatch.Operation) error {
    // Use compact format for 35.9% space savings
    compactData, err := compact.Encode(ops, compact.WithNumericOpcodes(true))
    if err != nil {
        return err
    }
    
    return database.Store(compactData) // Much smaller than JSON
}

func loadOperations() ([]jsonpatch.Operation, error) {
    compactData, err := database.Load()
    if err != nil {
        return nil, err
    }
    
    return compact.Decode(compactData)
}

🤝 Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

🎯 Credits

This project is a Golang port of json-joy/json-patch. Thanks to the original authors for their excellent work.

Original project: streamich/json-joy

📄 License

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

Documentation

Overview

Package jsonpatch provides comprehensive JSON Patch operations with generic type support.

Implements JSON mutation operations including:

Core API Functions:

  • ApplyOp: Apply a single operation
  • ApplyOps: Apply multiple operations
  • ApplyPatch: Apply a JSON Patch to a document (main generic API)
  • ValidateOperations: Validate an array of operations
  • ValidateOperation: Validate a single operation

Basic usage:

doc := map[string]any{"name": "John", "age": 30}
patch := []Operation{
	{"op": "replace", "path": "/name", "value": "Jane"},
	{"op": "add", "path": "/email", "value": "jane@example.com"},
}
result, err := ApplyPatch(doc, patch, WithMutate(false))

The library provides type-safe operations for any supported document type.

Example

Example demonstrates basic JSON Patch operations

package main

import (
	"encoding/json"
	"fmt"

	"github.com/kaptinlin/jsonpatch"
)

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

	// Create patch operations
	patch := []jsonpatch.Operation{
		// Add a new field
		map[string]interface{}{
			"op":    "add",
			"path":  "/user/active",
			"value": true,
		},
		// Update existing field
		map[string]interface{}{
			"op":    "replace",
			"path":  "/user/age",
			"value": 26,
		},
		// Add to settings
		map[string]interface{}{
			"op":    "add",
			"path":  "/settings/notifications",
			"value": true,
		},
	}

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

	// Print result
	resultJSON, _ := json.MarshalIndent(result.Doc, "", "  ")
	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
)

Operation type constants (string constants)

View Source
const (
	// JSON Patch (RFC 6902) operations
	OpAddCode     = internal.OpAddCode
	OpRemoveCode  = internal.OpRemoveCode
	OpReplaceCode = internal.OpReplaceCode
	OpCopyCode    = internal.OpCopyCode
	OpMoveCode    = internal.OpMoveCode
	OpTestCode    = internal.OpTestCode

	// String editing
	OpStrInsCode = internal.OpStrInsCode
	OpStrDelCode = internal.OpStrDelCode

	// Extra
	OpFlipCode = internal.OpFlipCode
	OpIncCode  = internal.OpIncCode

	// Slate.js
	OpSplitCode  = internal.OpSplitCode
	OpMergeCode  = internal.OpMergeCode
	OpExtendCode = internal.OpExtendCode

	// JSON Predicate
	OpContainsCode      = internal.OpContainsCode
	OpDefinedCode       = internal.OpDefinedCode
	OpEndsCode          = internal.OpEndsCode
	OpInCode            = internal.OpInCode
	OpLessCode          = internal.OpLessCode
	OpMatchesCode       = internal.OpMatchesCode
	OpMoreCode          = internal.OpMoreCode
	OpStartsCode        = internal.OpStartsCode
	OpUndefinedCode     = internal.OpUndefinedCode
	OpTestTypeCode      = internal.OpTestTypeCode
	OpTestStringCode    = internal.OpTestStringCode
	OpTestStringLenCode = internal.OpTestStringLenCode
	OpTypeCode          = internal.OpTypeCode
	OpAndCode           = internal.OpAndCode
	OpNotCode           = internal.OpNotCode
	OpOrCode            = internal.OpOrCode
)

Operation code constants (numeric constants)

View Source
const (
	JsonPatchTypeString  = internal.JsonPatchTypeString
	JsonPatchTypeNumber  = internal.JsonPatchTypeNumber
	JsonPatchTypeBoolean = internal.JsonPatchTypeBoolean
	JsonPatchTypeObject  = internal.JsonPatchTypeObject
	JsonPatchTypeInteger = internal.JsonPatchTypeInteger
	JsonPatchTypeArray   = internal.JsonPatchTypeArray
	JsonPatchTypeNull    = internal.JsonPatchTypeNull
)

Variables

View Source
var (
	IsValidJsonPatchType = internal.IsValidJsonPatchType
	GetJsonPatchType     = internal.GetJsonPatchType

	// Operation type checking functions
	IsJsonPatchOperation            = internal.IsJsonPatchOperation
	IsPredicateOperation            = internal.IsPredicateOperation
	IsFirstOrderPredicateOperation  = internal.IsFirstOrderPredicateOperation
	IsSecondOrderPredicateOperation = internal.IsSecondOrderPredicateOperation
	IsJsonPatchExtendedOperation    = internal.IsJsonPatchExtendedOperation
)

Re-export functions

View Source
var (
	ErrNoOperationDecoded    = errors.New("no operation decoded")
	ErrInvalidDocumentType   = errors.New("invalid document type")
	ErrConversionFailed      = errors.New("failed to convert result back to original type")
	ErrNoOperationResult     = errors.New("no operation result")
	ErrUnnecessaryConversion = errors.New("unnecessary type conversion")
)

Operation application errors

View Source
var (
	ErrNotArray             = errors.New("not an array")
	ErrEmptyPatch           = errors.New("empty operation patch")
	ErrInvalidOperation     = errors.New("invalid operation")
	ErrMissingPath          = errors.New("missing required field 'path'")
	ErrMissingOp            = errors.New("missing required field 'op'")
	ErrMissingValue         = errors.New("missing required field 'value'")
	ErrMissingFrom          = errors.New("missing required field 'from'")
	ErrInvalidPath          = errors.New("field 'path' must be a string")
	ErrInvalidOp            = errors.New("field 'op' must be a string")
	ErrInvalidFrom          = errors.New("field 'from' must be a string")
	ErrInvalidJSONPointer   = errors.New("invalid JSON pointer")
	ErrInvalidOldValue      = errors.New("invalid oldValue")
	ErrCannotMoveToChildren = errors.New("cannot move into own children")
	ErrInvalidIncValue      = errors.New("invalid inc value")
	ErrExpectedStringField  = errors.New("expected string field")
	ErrExpectedBooleanField = errors.New("expected field to be boolean")
	ErrExpectedIntegerField = errors.New("not an integer")
	ErrNegativeNumber       = errors.New("number is negative")
	ErrInvalidProps         = errors.New("invalid props field")
	ErrInvalidTypeField     = errors.New("invalid type field")
	ErrEmptyTypeList        = errors.New("empty type list")
	ErrInvalidType          = errors.New("invalid type")
	ErrValueMustBeString    = errors.New("value must be a string")
	ErrValueMustBeNumber    = errors.New("value must be a number")
	ErrValueMustBeArray     = errors.New("value must be an array")
	ErrValueTooLong         = errors.New("value too long")
	ErrInvalidNotModifier   = errors.New("invalid not modifier")
	ErrMatchesNotAllowed    = errors.New("matches operation not allowed")
	ErrMustBeArray          = errors.New("must be an array")
	ErrEmptyPredicateList   = errors.New("predicate list is empty")
	ErrEitherStrOrLen       = errors.New("either str or len must be set")
	ErrPosGreaterThanZero   = errors.New("expected pos field to be greater than 0")

	// Additional static errors for err113 compliance
	ErrInOperationValueMustBeArray = errors.New("in operation value must be an array")
	ErrExpectedValueToBeString     = errors.New("expected value to be string")
	ErrExpectedIgnoreCaseBoolean   = errors.New("expected ignore_case to be boolean")
	ErrExpectedFieldString         = errors.New("expected field to be string")
)

Base validation errors - define clearly and concisely

View Source
var WithMatcher = internal.WithMatcher
View Source
var WithMutate = internal.WithMutate

Functions

func ApplyOp

func ApplyOp[T internal.Document](doc T, operation internal.Op, opts ...internal.Option) (*internal.OpResult[T], error)

ApplyOp applies a single operation to a document with generic type support. It automatically detects the document type and applies the appropriate strategy. Returns an OpResult containing the patched document and old value.

Example usage:

// Struct
user := User{Name: "John", Age: 30}
result, err := ApplyOp(user, operation, WithMutate(false))
if err == nil {
	patchedUser := result.Doc // Type: User
	oldValue := result.Old    // Previous value
}

// Map
doc := map[string]any{"name": "John", "age": 30}
result, err := ApplyOp(doc, operation, WithMutate(true))

The function preserves the input type: struct input returns struct output, map input returns map output, etc.

func ApplyOpDirect

func ApplyOpDirect(operation internal.Op, doc interface{}) (internal.OpResult[any], error)

ApplyOpDirect applies an operation directly using the Op interface.

func ApplyOps

func ApplyOps[T internal.Document](doc T, operations []internal.Op, opts ...internal.Option) (*internal.PatchResult[T], error)

ApplyOps applies multiple operations to a document with generic type support. It automatically detects the document type and applies the appropriate strategy. Returns a PatchResult containing the patched document and operation results.

Example usage:

// Struct
user := User{Name: "John", Age: 30}
result, err := ApplyOps(user, operations, WithMutate(false))
if err == nil {
	patchedUser := result.Doc // Type: User
	opResults := result.Res   // Operation results
}

// Map
doc := map[string]any{"name": "John", "age": 30}
result, err := ApplyOps(doc, operations, WithMutate(true))

The function preserves the input type: struct input returns struct output, map input returns map output, etc.

func ApplyPatch

func ApplyPatch[T internal.Document](doc T, patch []internal.Operation, opts ...internal.Option) (*internal.PatchResult[T], error)

ApplyPatch applies a JSON Patch to any supported document type. It automatically detects the document type and applies the appropriate strategy. Returns a PatchResult containing the patched document and operation results.

Supported document types:

  • struct: Converted via JSON marshaling/unmarshaling
  • map[string]any: Applied directly using existing implementation
  • []byte: Parsed as JSON, patched, and re-encoded
  • string: Parsed as JSON string, patched, and re-encoded

Example usage:

// Struct
user := User{Name: "John", Age: 30}
result, err := ApplyPatch(user, patch)
if err == nil {
	patchedUser := result.Doc // Type: User
	operations := result.Res  // Operation results
}

// Map
doc := map[string]any{"name": "John", "age": 30}
result, err := ApplyPatch(doc, patch)
if err == nil {
	patchedDoc := result.Doc // Type: map[string]any
}

// JSON bytes
data := []byte(`{"name":"John","age":30}`)
result, err := ApplyPatch(data, patch)
if err == nil {
	patchedData := result.Doc // Type: []byte
}

The function preserves the input type: struct input returns struct output, map input returns map output, etc.

func DefaultOptions added in v0.2.0

func DefaultOptions() *internal.Options

DefaultOptions returns the default configuration for patch operations.

func GetOpCode

func GetOpCode(operation internal.Op) int

GetOpCode returns the operation code using the Op interface.

func GetOpPath

func GetOpPath(operation internal.Op) []string

GetOpPath returns the operation path using the Op interface.

func GetOpType

func GetOpType(operation internal.Op) internal.OpType

GetOpType returns the operation type using the Op interface.

func GetSecondOrderOps

func GetSecondOrderOps(predicate internal.SecondOrderPredicateOp) []internal.PredicateOp

GetSecondOrderOps returns sub-operations from a second-order predicate using the SecondOrderPredicateOp interface.

func IsNotPredicate

func IsNotPredicate(predicate internal.PredicateOp) bool

IsNotPredicate checks if a predicate is negated using the PredicateOp interface.

func TestPredicate

func TestPredicate(predicate internal.PredicateOp, doc interface{}) (bool, error)

TestPredicate tests a predicate operation using the PredicateOp interface.

func ToCompact

func ToCompact(operation internal.Op) (internal.CompactOperation, error)

ToCompact converts an operation to compact format using the Op interface.

func ToJSON

func ToJSON(operation internal.Op) (internal.Operation, error)

ToJSON converts an operation to JSON format using the Op interface.

func ValidateOp

func ValidateOp(operation internal.Op) error

ValidateOp validates an operation using the Op interface.

func ValidateOperation

func ValidateOperation(operation Operation, allowMatchesOp bool) error

ValidateOperation validates a single JSON Patch operation.

func ValidateOperations

func ValidateOperations(ops []Operation, allowMatchesOp bool) error

ValidateOperations validates an array of JSON Patch operations.

Types

type Document added in v0.2.0

type Document = internal.Document

Generic types for type-safe JSON Patch operations

type JsonPatchOptions

type JsonPatchOptions = internal.JsonPatchOptions

JSON Patch types

type JsonPatchTypes

type JsonPatchTypes = internal.JsonPatchTypes

JSON Patch types

type Op

type Op = internal.Op

Core types

type OpResult

type OpResult[T Document] = internal.OpResult[T]

Generic result types (requires Go 1.18+)

type OpType

type OpType = internal.OpType

Core types

type Operation

type Operation = internal.Operation

Core types

type Option added in v0.2.0

type Option = internal.Option

Generic types for type-safe JSON Patch operations

type Options added in v0.2.0

type Options = internal.Options

Generic types for type-safe JSON Patch operations

type PatchResult

type PatchResult[T Document] = internal.PatchResult[T]

type RegexMatcher

type RegexMatcher = internal.RegexMatcher

JSON Patch types

func CreateMatcherDefault

func CreateMatcherDefault(pattern string, ignoreCase bool) RegexMatcher

CreateMatcherDefault creates a default regular expression matcher.

Directories

Path Synopsis
codec
compact
Package compact implements a compact array-based codec for JSON Patch operations.
Package compact implements a compact array-based codec for JSON Patch operations.
json
Package json implements JSON codec for JSON Patch operations.
Package json implements JSON codec for JSON Patch operations.
examples
compact-codec
Package main demonstrates the compact codec functionality.
Package main demonstrates the compact codec functionality.
pkg
tests

Jump to

Keyboard shortcuts

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