1.1.5-jasny.1 • Published 10 months ago

@letsflow/jmespath v1.1.5-jasny.1

Weekly downloads
-
License
MPL-2.0
Repository
github
Last release
10 months ago

Build

@letsflow/jmespath

@letsflow/jmespath is a TypeScript implementation of the JMESPath spec.

JMESPath is a query language for JSON. It will take a JSON document as input and transform it into another JSON document given a JMESPath expression.

This fork extends the original specs, adding the following functionality;

Additionally, it adds the following functions:

  • if - Conditional expression
  • range - Generate a range of numbers or prefixed strings
  • to_object - Convert an array of key-value pairs into an object
  • json_serialize - Serialize a JSON value to a string
  • json_parse - Parse a JSON string into a JSON object
  • sha256 - Calculate the SHA-256 hash of a string
  • sha512 - Calculate the SHA-512 hash of a string
  • uuid - Generate a UUID v5
  • regex_test - Test if a string matches a regular expression
  • regex_match - Return the first match of a regular expression in a string
  • regex_match_all - Return all matches of a regular expression in a string
  • regex_replace - Replace parts of a string matching a regular expression with a replacement string

INSTALLATION

npm install @letsflow/jmespath

USAGE

search(data: JSONValue, expression: string): JSONValue

import { search } from '@letsflow/jmespath';

search(
  { foo: { bar: { baz: [0, 1, 2, 3, 4] } } },
  "foo.bar.baz[2]"
);

// OUTPUTS: 2

In the example we gave the search function input data of {foo: {bar: {baz: [0, 1, 2, 3, 4]}}} as well as the JMESPath expression foo.bar.baz[2], and the search function evaluated the expression against the input data to produce the result 2.

The JMESPath language can do a lot more than select an element from a list. Here are a few more examples:

import { search } from '@letsflow/jmespath';

const document = {
  foo: {
    bar: {
      baz: [0, 1, 2, 3, 4]
    }
  }
};

search(document, "foo.bar");
// OUTPUTS: { baz: [ 0, 1, 2, 3, 4 ] }
import { search } from '@letsflow/jmespath';

const document = {
  "foo": [
    { "first": "a", "last": "b" },
    { "first": "c", "last": "d" }
  ]
};

search(document, "foo[*].first")
// OUTPUTS: [ 'a', 'c' ]
import { search } from '@letsflow/jmespath';

const document = {
  "foo": [
    { "age": 20 },
    { "age": 25 },
    { "age": 30 },
    { "age": 35 },
    { "age": 40 }
  ]
}

search(document, "foo[?age > `30`]");
// OUTPUTS: [ { age: 35 }, { age: 40 } ]

compile(expression: string): ExpressionNodeTree

You can precompile all your expressions ready for use later on. the compile function takes a JMESPath expression and returns an abstract syntax tree that can be used by the TreeInterpreter function

import { compile, TreeInterpreter } from '@jmespath-community/jmespath';

const ast = compile('foo.bar');

TreeInterpreter.search(ast, { foo: { bar: 'BAZ' } })
// RETURNS: "BAZ"

EXTENSIONS TO ORIGINAL SPEC

Custom functions

registerFunction(functionName: string, customFunction: RuntimeFunction, signature: InputSignature[]): void

Extend the list of built-in JMESpath expressions with your own functions.

  import {search, registerFunction, TYPE_NUMBER} from '@letsflow/jmespath'

  search({ foo: 60, bar: 10 }, 'divide(foo, bar)')
  // THROWS ERROR: Error: Unknown function: divide()

  registerFunction(
    'divide', // FUNCTION NAME
    (resolvedArgs) => {   // CUSTOM FUNCTION
      const [dividend, divisor] = resolvedArgs;
      return dividend / divisor;
    },
    [{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER] }] //SIGNATURE
  );

  search({ foo: 60, bar: 10 }, 'divide(foo, bar)');
  // OUTPUTS: 6

Optional arguments are supported by setting {..., optional: true} in argument signatures

  registerFunction(
    'divide',
    (resolvedArgs) => {
      const [dividend, divisor] = resolvedArgs;
      return dividend / divisor ?? 1; //OPTIONAL DIVISOR THAT DEFAULTS TO 1
    },
    [{ types: [TYPE_NUMBER] }, { types: [TYPE_NUMBER], optional: true }] //SIGNATURE
  );

  search({ foo: 60, bar: 10 }, 'divide(foo)');
  // OUTPUTS: 60

Root value access

Use $ to access the document root.

search({foo: { bar: 999 }, baz: [1, 2, 3]}, '$.baz[*].[@, $.foo.bar]')

// OUTPUTS:
// [ [ 1, 999 ], [ 2, 999 ], [ 3, 999 ] ]

Number literals

Numbers in the root scope are treated as number literals. This means that you don't need to quote numbers with backticks.

search([{"bar": 1}, {"bar": 10}], '[?bar==10]')

// OUTPUTS;
// [{"bar": 10}]

You can also use numbers in arithmetic operations

search({}, '16 + 26'); // 42

Additional Functions

if

Syntax:

if(condition, thenValue, elseValue?)

Description:
Returns thenValue if condition is true, otherwise returns elseValue. If elseValue is not provided, it defaults to null.

Example:

if(@ > 10, "large", "small")

get

Syntax:

get(object, key, defaultValue?)

Description: Returns the value of a key in an object.

Example:

get({ key: 'value' }, 'key')                 // "value"
get({ key: 'value' }, 'missing')             // null
get({ key: 'value' }, 'missing', 'default')  // "default"

range

Syntax:

range(start, end, prefix?)

Description:
Generates an array of numbers or prefixed strings from start to end - 1. If prefix is provided, each number is prefixed.

Example:

range(5)                // [0, 1, 2, 3, 4]
range(1, 5)             // [1, 2, 3, 4]
range(1, 5, 'item_')    // ["item_1", "item_2", "item_3", "item_4"]

to_object

Syntax:

to_object(entries)

Description:
Converts an array of key-value pairs into an object.

Example:

to_object([['key1', 'value1'], ['key2', 'value2']])
// { "key1": "value1", "key2": "value2" }

[ 'value1', 'value2'] | to_object(zip(range(1, length(@) + 1, 'key'), @))
// { "key1": "value1", "key2": "value2" }

json_serialize

Syntax:

json_serialize(value)

Uses a deterministic version of JSON.stringify to serialize the value.

Description:
Serializes a JSON value to a string.

Example:

json_serialize({ key: 'value' })
// "{\"key\":\"value\"}"

json_parse

Syntax:

json_parse(string)

Description:
Parses a JSON string into a JSON object.

Example:

json_parse("{\"key\":\"value\"}")
// { "key": "value" }

sha256

Syntax:

sha256(string)

Description:
Calculates the SHA-256 hash of a string and returns it as a hexadecimal string.

Example:

sha256('hello')
// "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

sha512

Syntax:

sha512(string)

Description:
Calculates the SHA-512 hash of a string and returns it as a hexadecimal string.

Example:

sha512('hello')
// "9b71d224bd62f3785d96d46ad3ea3d73319b0c44e59b202205c5d235a0a6caa5a3b36f8c0ab9d45df9215bf07d4d1552c0b1f8bd2671c8a7a3d126f457d79d72"

uuid

Syntax:

uuid(name?, namespace?)

Description:
Generates a version 5 UUID.

UUID v5 is consistent. It creates a UUID based on the SHA hash of the input. This means that any given combination of input and namespace will result in the same UUID, every time.

Example:

uuid('example') // v5 UUID
uuid('example', '6ba7b810-9dad-11d1-80b4-00c04fd430c8') // v5 UUID with namespace

name must be a string. Use json_serialize() to convert a JSON object to a string. namespace must be a UUID string. By default, it uses the NIL UUID.

The UUID RFC pre-defines four namespaces

  • NameSpace_DNS: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
  • NameSpace_URL: 6ba7b811-9dad-11d1-80b4-00c04fd430c8
  • NameSpace_OID: 6ba7b812-9dad-11d1-80b4-00c04fd430c8
  • NameSpace_X500: 6ba7b814-9dad-11d1-80b4-00c04fd430c8

regex_test

Syntax:

regex_test(regex, string)

Description:
Tests if a string matches a given regular expression.

Example:

regex_test('/^hello/', 'hello world') // true

regex_test('/^hello/', 'HELLO world') // false
regex_test('/^hello/i', 'HELLO world') // true

regex_match

Syntax:

regex_match(regex, string)

Description:
Returns the first match of a regular expression in a string as an array.

Example:

regex_match('/hello (\\w+)/', 'hello world')
// ["hello world", "world"]

regex_match('/\\w+/g', 'hello world')
// ["hello", "world"]

regex_match_all

Syntax:

regex_match_all(regex, string)

Description:
Returns all matches of a regular expression in a string as an array of arrays.

Example:

regex_match_all('/(\\w+)=(\d+)/g', 'foo=24 bar=99')
// [["foo=24", "foo", "24"], ["bar=99", "bar", "99"]]

regex_replace

Syntax:

regex_replace(regex, replacement, string)

Description:
Replaces parts of a string matching a regular expression with a replacement string.

Example:

regex_replace('/world/', 'universe', 'hello world')
// "hello universe"

regex_count

Syntax:

regex_count(regex, string)

Description: Counts the number of matches of a regular expression in a string.

Example:

regex_count('/\\w+/g', 'hello world')
// 2

More Resources

The example above only shows a small amount of what a JMESPath expression can do. If you want to take a tour of the language, the best place to go is the JMESPath Tutorial.

The full JMESPath specification can be found on the JMESPath site.

1.1.5-jasny.1

10 months ago

1.1.4-jasny.4

11 months ago

1.1.4-jasny.3

11 months ago

1.1.4-jasny.2

11 months ago

1.1.4-jasny.1

11 months ago