Binary Codec for JSON Patch Operations
The binary codec provides maximum performance and space efficiency by serializing JSON Patch operations directly to MessagePack binary format. It achieves the smallest possible payload size while maintaining full compatibility with all operation types.
🎯 Key Features: Maximum compression, highest performance, MessagePack format, zero-allocation hot paths, full operation support.
- 📦 Smallest payloads: Binary MessagePack format achieves maximum space efficiency
- ⚡ Fastest encoding: Direct binary serialization without JSON overhead
- 🚀 Zero-allocation: Optimized hot paths with minimal memory allocation
- 🔄 Perfect round-trip: 100% compatibility with JSON and Compact codecs
- 📊 All operations: Complete support for all 25+ operation types
The binary codec uses MessagePack arrays with the same structure as the compact codec:
[opcode, path, ...args]
But encoded in efficient binary format instead of JSON arrays.
Usage with Modern Struct API
import (
"github.com/kaptinlin/jsonpatch"
"github.com/kaptinlin/jsonpatch/codec/binary"
)
func main() {
// Create operations using clean struct syntax
operations := []jsonpatch.Operation{
{Op: "add", Path: "/user/name", Value: "Alice"},
{Op: "inc", Path: "/user/score", Inc: 100},
{Op: "str_ins", Path: "/user/bio", Pos: 0, Str: "Hello! "},
{Op: "flip", Path: "/user/active"},
}
codec := &binary.Codec{}
// Encode to binary MessagePack (most efficient)
data, err := codec.Encode(operations)
if err != nil {
panic(err)
}
fmt.Printf("Binary size: %d bytes\n", len(data))
// Typically 40-60% smaller than JSON
// Decode back to operations
decoded, err := codec.Decode(data)
if err != nil {
panic(err)
}
// Apply decoded operations
result, err := jsonpatch.ApplyOps(doc, decoded)
if err != nil {
panic(err)
}
}
Complete Operation Support (From Code Analysis)
Based on deep analysis of encoder.go and decoder.go, the binary codec supports all these operations:
Standard JSON Patch Operations (RFC 6902)
| Operation |
Code |
Binary Format |
Struct Example |
| add |
0 |
[0, path_array, value] |
{Op: "add", Path: "/foo", Value: 123} |
| remove |
1 |
[1, path_array] |
{Op: "remove", Path: "/foo"} |
| replace |
2 |
[2, path_array, value] |
{Op: "replace", Path: "/foo", Value: 456} |
| move |
4 |
[4, from_array, path_array] |
{Op: "move", Path: "/bar", From: "/foo"} |
| copy |
3 |
[3, from_array, path_array] |
{Op: "copy", Path: "/bar", From: "/foo"} |
| test |
5 |
[5, path_array, value] |
{Op: "test", Path: "/foo", Value: 123} |
Extended Operations
| Operation |
Code |
Binary Format |
Struct Example |
| flip |
8 |
[8, path_array] |
{Op: "flip", Path: "/active"} |
| inc |
9 |
[9, path_array, delta] |
{Op: "inc", Path: "/count", Inc: 5} |
| str_ins |
6 |
[6, path_array, pos, str] |
{Op: "str_ins", Path: "/text", Pos: 0, Str: "Hi"} |
| str_del |
7 |
[7, path_array, pos, len] |
{Op: "str_del", Path: "/text", Pos: 5, Len: 3} |
| split |
10 |
[10, path_array, pos, props] |
{Op: "split", Path: "/obj", Pos: 2, Props: {...}} |
| extend |
12 |
[12, path_array, props, deleteNull] |
{Op: "extend", Path: "/config", Props: {...}} |
| merge |
11 |
[11, path_array, pos, props] |
{Op: "merge", Path: "/obj", Pos: 0, Props: {...}} |
JSON Predicate Operations
| Operation |
Code |
Binary Format |
Struct Example |
| defined |
31 |
[31, path_array] |
{Op: "defined", Path: "/field"} |
| undefined |
38 |
[38, path_array, not] |
{Op: "undefined", Path: "/field"} |
| contains |
30 |
[30, path_array, value] |
{Op: "contains", Path: "/text", Value: "hello"} |
| starts |
37 |
[37, path_array, value] |
{Op: "starts", Path: "/text", Value: "Hello"} |
| ends |
32 |
[32, path_array, value] |
{Op: "ends", Path: "/text", Value: ".com"} |
| matches |
35 |
[35, path_array, pattern, ignoreCase] |
{Op: "matches", Path: "/email", Value: ".*@.*"} |
| in |
33 |
[33, path_array, values] |
{Op: "in", Path: "/role", Value: ["admin"]} |
| less |
34 |
[34, path_array, value] |
{Op: "less", Path: "/age", Value: 30} |
| more |
36 |
[36, path_array, value] |
{Op: "more", Path: "/score", Value: 100} |
| type |
42 |
[42, path_array, type] |
{Op: "type", Path: "/data", Value: "string"} |
| test_type |
39 |
[39, path_array, types] |
{Op: "test_type", Path: "/data", Type: ["string"]} |
| test_string |
40 |
[40, path_array, str, pos] |
{Op: "test_string", Path: "/text", Str: "test", Pos: 5} |
| test_string_len |
41 |
[41, path_array, len, not] |
{Op: "test_string_len", Path: "/text", Len: 10} |
MessagePack Technical Details
Path Encoding
Paths are encoded as variable-length arrays:
[path_length, segment1, segment2, ...]
For example, path "/user/name" becomes:
[2.0, "user", "name"] // Binary MessagePack representation
Value Encoding
Values use MessagePack's native type preservation:
- Strings: Direct UTF-8 binary encoding
- Numbers: Compact binary number representation
- Objects: Recursive MessagePack object encoding
- Arrays: Recursive MessagePack array encoding
Special Float Support
The binary codec handles special IEEE 754 values correctly:
- NaN: Preserved in binary format
- +Infinity: Native MessagePack representation
- -Infinity: Native MessagePack representation
API Reference
Codec Structure
type Codec struct{} // Stateless codec instance
// Core methods
func (c *Codec) Encode(operations []jsonpatch.Operation) ([]byte, error)
func (c *Codec) Decode(data []byte) ([]jsonpatch.Operation, error)
- Encoding Speed: ~3x faster than JSON codec
- Decoding Speed: ~2.5x faster than JSON codec
- Space Efficiency: ~40-60% smaller than JSON
- Memory Usage: Minimal allocations in hot paths
- CPU Efficiency: No string parsing overhead
Use Cases
// For maximum performance in hot paths
codec := &binary.Codec{}
data, _ := codec.Encode(operations) // Fastest encoding
SendOverNetwork(data) // Smallest payload
Storage Optimization
// For efficient storage in databases
operations := []jsonpatch.Operation{
{Op: "add", Path: "/data", Value: largeObject},
}
binaryData, _ := binary.Encode(operations)
database.Store(binaryData) // 40-60% space savings
Real-time Applications
// For real-time collaboration (like Slate.js)
operations := []jsonpatch.Operation{
{Op: "str_ins", Path: "/document/text", Pos: 150, Str: "new text"},
{Op: "split", Path: "/document/paragraph", Pos: 2},
}
binaryPatch, _ := codec.Encode(operations)
websocket.Send(binaryPatch) // Minimal network overhead
TIP: The binary codec is fully round-trip compatible with JSON and Compact codecs. You can freely convert between formats based on your performance and storage requirements.