Package Exports
- @winglet/json
- @winglet/json/path
- @winglet/json/path-common
- @winglet/json/pointer
- @winglet/json/pointer-common
- @winglet/json/pointer-escape
- @winglet/json/pointer-manipulator
- @winglet/json/pointer-patch
Readme
@winglet/json
Overview
@winglet/json is a TypeScript library for safe and efficient JSON data manipulation. It provides structured JSON data processing by strictly adhering to RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch) standards.
Key Features
- Type Safety: Full TypeScript support with compile-time type validation
- Standards Compliant: Complete implementation of RFC 6901 (JSON Pointer) and RFC 6902 (JSON Patch)
- Security: Built-in protection against prototype pollution attacks
- Flexibility: Immutable and strict mode options for different use cases
- Performance: Optimized algorithms supporting large-scale JSON data processing
Installation
# Using npm
npm install @winglet/json
# Using yarn
yarn add @winglet/json
# Using pnpm
pnpm add @winglet/jsonSub-path Imports
This package supports sub-path imports to enable more granular imports and optimize bundle size. You can import specific modules directly without importing the entire package:
// Main exports (all JSONPointer and JSONPath utilities)
import { getValue, setValue } from '@winglet/json';
// JSONPath utilities
import { JSONPath } from '@winglet/json/path';
// JSONPointer utilities
import {
getValue,
setValue,
escapePath,
unescapePath,
compare,
applyPatch,
difference,
mergePatch
} from '@winglet/json/pointer';Available Sub-paths
Based on the package.json exports configuration:
@winglet/json- Main exports (all JSONPointer and JSONPath utilities)@winglet/json/path- JSONPath constants and utilities@winglet/json/path-common- JSONPath common utilities@winglet/json/pointer- JSONPointer core utilities@winglet/json/pointer-common- JSONPointer common utilities@winglet/json/pointer-escape- JSONPointer escaping utilities (escapePath, unescapePath)@winglet/json/pointer-manipulator- JSONPointer manipulation functions (getValue, setValue)@winglet/json/pointer-patch- JSONPointer patch operations (compare, applyPatch, difference, mergePatch)
Compatibility
This package is written using ECMAScript 2020 (ES2020) syntax.
Supported Environments:
- Node.js 14.0.0 or higher
- Modern browsers (with ES2020 support)
For Legacy Environment Support: Use transpilers like Babel to convert the code to match your target environment.
API Reference
JSONPath
Provides special character constants used in JSONPath expressions.
Supported Operators
JSONPath.Root($): Root node of the JSON documentJSONPath.Parent(_): Parent node of the current nodeJSONPath.Current(@): Currently processing nodeJSONPath.Child(.): Child node access operatorJSONPath.Filter(#): Filter condition operator
JSONPointer
Provides a complete JSON Pointer implementation that fully complies with RFC 6901.
Core Features
Data Manipulation
import { getValue } from '@winglet/json';
const data = {
user: {
profile: {
name: 'Vincent',
age: 30,
},
},
};
const name = getValue(data, '/user/profile/name');
// Result: "Vincent"import { setValue } from '@winglet/json';
const data = { user: { profile: {} } };
const result = setValue(data, '/user/profile/email', 'vincent@example.com');
// Result: { user: { profile: { email: "vincent@example.com" } } }Escape Handling
import { escapePath } from '@winglet/json';
const escaped = escapePath('path/with~special');
// Result: "path~1with~0special"import { unescapePath } from '@winglet/json';
const unescaped = unescapePath('path~1with~0special');
// Result: "path/with~special"JSON Patch Operations
import { compare } from '@winglet/json';
const source = { name: 'John', age: 30, city: 'NYC' };
const target = { name: 'John', age: 31, country: 'USA' };
const patches = compare(source, target);
// Result:
// [
// { op: "replace", path: "/age", value: 31 },
// { op: "remove", path: "/city" },
// { op: "add", path: "/country", value: "USA" }
// ]import { applyPatch } from '@winglet/json';
const source = { name: 'John', age: 30 };
const patches = [
{ op: 'replace', path: '/age', value: 31 },
{ op: 'add', path: '/city', value: 'NYC' },
];
const result = applyPatch(source, patches);
// Result: { name: "John", age: 31, city: "NYC" }import { difference } from '@winglet/json';
const source = { name: 'John', age: 30, city: 'NYC' };
const target = { name: 'John', age: 31, country: 'USA' };
const mergePatch = difference(source, target);
// Result: { age: 31, city: null, country: "USA" }import { mergePatch } from '@winglet/json';
const source = { name: 'John', age: 30, temp: 'data' };
const patch = { age: 31, temp: null, city: 'NYC' };
const result = mergePatch(source, patch);
// Result: { name: "John", age: 31, city: "NYC" }Configuration Options
CompareOptions
interface CompareOptions {
strict?: boolean; // Strict comparison mode (default: false)
immutable?: boolean; // Immutable mode (default: true)
}ApplyPatchOptions
interface ApplyPatchOptions {
strict?: boolean; // Strict application mode (default: false)
immutable?: boolean; // Immutable mode (default: true)
protectPrototype?: boolean; // Prototype protection (default: true)
}Usage Examples
Basic Usage
import { applyPatch, compare, getValue, setValue } from '@winglet/json';
// Complex JSON data
const data = {
users: [
{ id: 1, name: 'Alice', preferences: { theme: 'dark' } },
{ id: 2, name: 'Bob', preferences: { theme: 'light' } },
],
settings: {
app: { version: '1.0.0' },
},
};
// Value retrieval
const theme = getValue(data, '/users/0/preferences/theme');
console.log(theme); // "dark"
// Value setting
const updated = setValue(data, '/settings/app/version', '1.1.0');
// Change comparison
const patches = compare(data, updated);
console.log(patches);
// [{ op: "replace", path: "/settings/app/version", value: "1.1.0" }]Advanced Usage - Immutability and Security
import { applyPatch } from '@winglet/json';
const data = { user: { role: 'user' } };
const patches = [
{ op: 'add', path: '/user/permissions', value: ['read', 'write'] },
{ op: 'replace', path: '/user/role', value: 'admin' },
];
// Safe patch application (prevents prototype pollution)
const result = applyPatch(data, patches, {
immutable: true, // Preserve original data
protectPrototype: true, // Prevent prototype pollution
strict: true, // Strict validation
});
console.log(data); // Original data preserved
console.log(result); // New modified objectJSON Merge Patch Usage
import { difference, mergePatch } from '@winglet/json';
const source = {
user: { name: 'Alice', age: 25, role: 'admin', temp: 'data' },
settings: { theme: 'dark' },
};
const target = {
user: { name: 'Bob', age: 25, permissions: ['read', 'write'] },
settings: { theme: 'light', language: 'en' },
};
// Generate JSON Merge Patch representing differences between two objects
const patch = difference(source, target);
console.log(patch);
// {
// user: { name: "Bob", role: null, temp: null, permissions: ["read", "write"] },
// settings: { theme: "light", language: "en" }
// }
// Apply JSON Merge Patch
const result = mergePatch(source, patch);
console.log(result);
// {
// user: { name: "Bob", age: 25, permissions: ["read", "write"] },
// settings: { theme: "light", language: "en" }
// }Array Manipulation
import { getValue, setValue } from '@winglet/json';
const data = {
items: ['apple', 'banana', 'cherry'],
};
// Array element access
const secondItem = getValue(data, '/items/1');
// Result: "banana"
// Add element to end of array (using RFC 6901 "-" syntax)
const withNewItem = setValue(data, '/items/-', 'date');
// Result: { items: ["apple", "banana", "cherry", "date"] }Error Handling
import { JSONPointerError, getValue } from '@winglet/json';
try {
const value = getValue({}, '/nonexistent/path');
} catch (error) {
if (error instanceof JSONPointerError) {
console.error('JSON Pointer Error:', error.message);
console.error('Error Code:', error.code);
console.error('Additional Details:', error.details);
}
}Performance Considerations
- Large Data: Consider using
immutable: falseoption when dealing with deeply nested objects or large arrays - Frequent Changes: Use
strict: falseto improve performance when applying many patches sequentially - Memory Usage: Immutable mode uses more memory but ensures safety
Contributing
If you'd like to contribute to this project:
- Create an issue for bug reports or feature suggestions
- Submit pull requests with improvements
- Include test cases with your submissions
License
This repository is provided under the MIT License. See the LICENSE file for details.
Related Standards
- RFC 6901 - JavaScript Object Notation (JSON) Pointer
- RFC 6902 - JavaScript Object Notation (JSON) Patch
- RFC 7396 - JSON Merge Patch
- JSONPath - XPath for JSON
Contact
For questions or suggestions about this project, please create a GitHub issue.