@sagold/json-pointer

json-pointer implementation following RFC 6901 to work with serializable paths into javascript data structures.

api | usage examples | fragment identifier | breaking changes

**install** `yarn add @sagold/json-pointer` **usage** ```js import { get, set, remove } from '@sagold/json-pointer'; const data = {}; get(data, '/path/to/nested/item'); // undefined set(data, '/path/to/nested/item', 123); // { path: { to: { nested: { item: 123 }}} remove(data, '/path/to/nested/item'); // { path: { to: { nested: { }}} ``` ## API As the _error handling_ is not further specified, this implementation will return `undefined` for any invalid pointer/missing data, making it very convenient to work with uncertain data. | method | description | ----------------------------------------- | ------------------------------------------------------------- | get(data, pointer) -> value | returns the value at given pointer | set(data, pointer, value) -> data | sets the value at the given path | remove(data, pointer) -> data | removes a property from data | join(...pointers) -> pointer | joins multiple pointers to a single one | split(pointer) -> [array] | returns a json-pointer as an array | splitLast(pointer) -> [pointer, property] | returns parent-pointer and last property > The methods `get`, `set`, `remove` and `join` also accept a list of properties as pointer. Using join with a list > of properties, its signature changes to `join(properties:string[], isURI=false) -> string` ## Usage Examples ### get > get(data:object|array, pointer:string|array, defaultValue:any) -> value:any returns nested values ```js import pointer from '@sagold/json-pointer'; const data = { parent: { child: { title: 'title of child' } } } const titleOfChild = pointer.get(data, '/parent/child/title'); // output: 'title of child' console.log(pointer.get(data, '/parent/missing/path')); // output: undefined ``` and may optionally return a default value with ```js import pointer from '@sagold/json-pointer'; const value = pointer.get({}, "/invalid/value", 42); console.log(value); // output: 42 ``` `get` also accepts a list of properties as pointer (e.g. split-result) ```js const titleOfChild = pointer.get(data, ['parent', 'child', 'title']); // output: 'title of child' console.log(pointer.get(data, ['parent', 'missing', 'path'])); // output: undefined ``` ### set > set(data:object|array, pointer:string|array, value:any) -> data:object|array changes a nested value ```js import pointer from '@sagold/json-pointer'; var data = { parent: { children: [ { title: 'title of child' } ] } }; pointer.set(data, '/parent/children/1', { title: 'second child' }); console.log(data.parent.children.length); // output: 2 ``` and may be used to build data ```js import pointer from '@sagold/json-pointer'; const data = pointer.set({}, '/list/[]/value', 42); console.log(data); // output: { list: [ { value: 42 } ] } ``` `set` also accepts a list of properties as pointer (e.g. split-result) ```js import pointer from '@sagold/json-pointer'; const data = pointer.set({}, ['list', '[]', 'value'], 42); console.log(data); // output: { list: [ { value: 42 } ] } ``` ### remove > remove(data:object|array, pointer:string|array) -> data:object|array deletes a nested property or item ```js import pointer from '@sagold/json-pointer'; const data = pointer.remove({ parent: { arrayOrObject: [ 0, 1 ] }}, '/parent/arrayOrObject/1'); console.log(data.parent.arrayOrObject); // output: [0] ``` `remove` also accepts a list of properties as pointer (e.g. split-result) ```js import pointer from '@sagold/json-pointer'; const data = pointer.remove({ parent: { arrayOrObject: [ 0, 1 ] }}, ['parent', 'arrayOrObject', '1']); console.log(data.parent.arrayOrObject); // output: [0] ``` ### split > split(pointer:string) -> properties:array returns a json-pointer as a list of (escaped) properties ```js import pointer from '@sagold/json-pointer'; const list = pointer.split('/parent/arrayOrObject/1'); console.log(list); // output: ['parent', 'arrayOrObject', '1'] ``` In order to resolve a list of properties, you can directly pass the list to `get`, `set` or `remove` ```js import pointer from '@sagold/json-pointer'; const data = { a: { b: true } }; const list = pointer.split('/a/b'); console.log(pointer.get(data, list)); // output: true ``` ### splitLast > splitLast(pointer:string) -> [pointer, property] separates json-pointers last property and returns both values as [parent-pointer, property] ```js import pointer from '@sagold/json-pointer'; const [parent, property] = pointer.splitLast('/parent/arrayOrObject/1'); console.log(parent); // output: '/parent/arrayOrObject' console.log(property); // output: '1' ``` ### join > join(...pointers:string[]) -> pointer:string joins all arguments to a valid json pointer ```js import pointer from '@sagold/json-pointer'; const key = 'my key'; console.log(pointer.join('root', key, '/to/target')); // output: '/root/my key/to/target' ``` and joins relative pointers as expected ```js import pointer from '@sagold/json-pointer'; console.log(pointer.join('/path/to/value', '../object')); // output: '/path/to/object' ``` in order to join an array received from split, you can use `join(properties:string[], isURI=false) -> string` to retrieve a valid pointer ```js import pointer from '@sagold/json-pointer'; const list = pointer.split('/my/path/to/child'); list.pop(); console.log(pointer.join(list)); // output: '/my/path/to' ``` To join an array of pointers, you must use it with `join(...pointers)` or all pointers will be treated as properties: ```js import pointer from '@sagold/json-pointer'; const path = pointer.join(...['/path/to/value', '../object']); console.log(path); // output: '/path/to/object' // passing the array directly, will treat each entry as a property, which will be escaped and resolves to: pointer.join(['/path/to/value', '../object']); // output: '/~1path~1to~1value/..~1object' ``` ## Fragment identifier All methods support a leading uri fragment identifier (#), which will ensure that property-values are uri decoded when resolving the path within data. This also ensures that any pointer is returned uri encoded with a leading `#`. e.g. ```js import pointer from '@sagold/json-pointer'; // get const value = pointer.get({ 'my value': true }, '#/my%20value'); console.log(value); // output: true // join const pointer = pointer.join('#/my value/to%20parent', '../to~1child'); console.log(pointer); // output: '#/my%20value/to~1child' // join an array of properties const uriPointer = pointer.join(['my value', 'to~1child'], isURI = true); console.log(uriPointer); // output: '#/my%20value/to~1child' ``` Additionally `join(...pointers, isURI)` may be used to enforce the pointer type, which is helpful in sanitizing inputs ```js const uriPointer = pointer.join('my pointer', 'to', 'property', isURI = true); console.log(uriPointer); // output: '#/my%20pointer/to/property' const uriSimple = pointer.join('/my pointer/to/property', isURI = true); console.log(uriSimple); // output: '#/my%20pointer/to/property' const pointer = pointer.join('#/my pointer', 'to', 'property', isURI = false); console.log(pointer); // output: '/my pointer/to/property' ``` ## Breaking Changes - 2022/12/02 with `v5`, package has been renamed to `json-pointer` and published under `@sagold/json-pointer` - 2020/11/09 with `v4`, `pointer.delete` has been renamed to `remove`