Introduction
Since the Vancouver release, there is a new API GlideJsonPath
[1] that allows to query a JSON string with jsonPath[2].
The jsonPath can be viewed as the JSON equivalent of the XPath for XML. It allows to perform queries like $.library.bools.*.title
for example. Jsonpath is offically a RFC since february 2024[^3]
I suspect the under the hood, it use the implementation JsonPath[5].
Usage of the GlideJsonPath
The official documentation[1] is very succint and there is a couple of extra information that can help:
- the
GlideJsonPath
expect a String, not an object as parameter - the
read
function seems to return an array of JavaObject, which are not the same as the javascript objects. It might need some kind of converstion before being able to use it.
To ease the usage, I suggest to use a function like this one, that accept an Object as parameter and return also a javascript Object:
/*
@name executeJSONpath
@description execute the JSON path on the given object and return the result
@param {Object} [obj] - Object on which to execute the json path
@param {String} [jsonPath] - Json path to use for the query
@return {Object} Object after applying the jsonPath read
*/
function executeJSONpath (obj, path){
try{
// Convert the object to a JSON string
var jsonString = JSON.stringify(obj);
// call and retrieve the result from GlideJsonPath
var gjp = new GlideJsonPath(jsonString);
var gjpResult = gjp.read(path);
return gjpResult;
} catch (exception){
gs.error("Error in function 'executeJSONpath' : " + exception + JSON.stringify(exception));
}
}
##JSON path syntax
The JSON path syntax can be summarized as follow:
dot-notation or bracket-notation
the JsonPath can use either a dot-notation, like $.store.book[0].title
or a bracker-notation $['store']['book'][0]['title']
It can also use a mix of both, like this example: $['store']['book'][0].title
Also in the bracket notation, it is possibe to specify mutliple attribute to retrieve, for example: `$[‘store’][‘book’][0].[“title”, “author”]
Elements
The JsonPath can use the following elements [2][3][5]:
$
: The root element to query. This starts all path expressions.@
: The current node being processed by a filter predicate.*
: Wildcard. Available anywhere a name or numeric are required...
: Deep scan. Available anywhere a name is required..<name>
: Dot-notated child['<name>' (, '<name>')]
: Bracket-notated child or children[<number> (, <number>)]
: Array index or indexes[start:end]
: Array slice operator[?(<expression>)]
: Filter expression. Expression must evaluate to a boolean value.
Functions
The specification gives several functions (min()
, max()
, …), but unfortunately it seems thay are not supported by the ServiceNow implementation.
Operators
For use in the expression, we can use the “classic” operator like “==”, “!=”, “>”, “=>”, etc. It also supports:
=~
: left matches regular expression [?(@.name =~ /foo.*?/i)]in
: left exists in right [?(@.size in [‘S’, ‘M’])]nin
: left does not exists in rightsubsetof
: left is a subset of right [?(@.sizes subsetof [‘S’, ‘M’, ‘L’])]anyof
: left has an intersection with right [?(@.sizes anyof [‘M’, ‘L’])]noneof
: left has no intersection with right [?(@.sizes noneof [‘M’, ‘L’])]size
: size of left (array or string) should match rightempty
: left (array or string) should be empty
Limitations
- functions: functions are not supported, as stated above
- unique values: There is not way to ask for unique values: there can be duplicated entries (for example when listing all categories)
Example of JSON path
This table show the result of various jsonPath from the GitHub JsonPath documentation[5].
When creating a new jsonPath, you can use this online evaluator[4] to test it
Description | JsonPath | Result |
---|---|---|
The authors of all books | $.store.book[*].author | [ “Nigel Rees”, “Evelyn Waugh”, “Herman Melville”, “J. R. R. Tolkien”] |
The authors and category of all books | $.store.book[*].[‘author’,’title’] | [ { “author”: “Nigel Rees”, “title”: “Sayings of the Century” }, { “author”: “Evelyn Waugh”, “title”: “Sword of Honour” }, { “author”: “Herman Melville”, “title”: “Moby Dick” }, { “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings” }] |
Title of first book, mixing dot and bracket | $[‘store’][‘book’][0].title | [ “Sayings of the Century”] |
All authors | $..author | [ “Nigel Rees”, “Evelyn Waugh”, “Herman Melville”, “J. R. R. Tolkien”] |
All things, both books and bicycles | $.store.* | [ [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }, { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 } ], { “color”: “red”, “price”: 19.95 }] |
The price of everything | $.store..price | [ 8.95, 12.99, 8.99, 22.99, 19.95] |
The third book | $..book[2] | [ { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }] |
The second to last book | $..book[-2] | [ { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }] |
The first two books | $..book[0,1] | [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }] |
All books from index 0 (inclusive) until index 2 (exclusive) | $..book[:2] | [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }] |
All books from index 1 (inclusive) until index 2 (exclusive) | $..book[1:2] | [ { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }] |
Last two books | $..book[-2:] | [ { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 }] |
All books from index 2 (inclusive) to last | $..book[2:] | [ { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 }] |
All books with an ISBN number | $..book[?(@.isbn)] | [ { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 }] |
All books in store cheaper than 10 | $.store.book[?(@.price < 10)] | [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }] |
All books in store that are not ’expensive’ | $..book[?(@.price <= $[’expensive’])] | [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }] |
All books matching regex (ignore case) | $..book[?(@.author =~ /.*REES/i)] | [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }] |
Give me everything | $..* | [ { “book”: [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }, { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 } ], “bicycle”: { “color”: “red”, “price”: 19.95 } }, 10, [ { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }, { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 } ], { “color”: “red”, “price”: 19.95 }, { “category”: “reference”, “author”: “Nigel Rees”, “title”: “Sayings of the Century”, “price”: 8.95 }, { “category”: “fiction”, “author”: “Evelyn Waugh”, “title”: “Sword of Honour”, “price”: 12.99 }, { “category”: “fiction”, “author”: “Herman Melville”, “title”: “Moby Dick”, “isbn”: “0-553-21311-3”, “price”: 8.99 }, { “category”: “fiction”, “author”: “J. R. R. Tolkien”, “title”: “The Lord of the Rings”, “isbn”: “0-395-19395-8”, “price”: 22.99 }, “reference”, “Nigel Rees”, “Sayings of the Century”, 8.95, “fiction”, “Evelyn Waugh”, “Sword of Honour”, 12.99, “fiction”, “Herman Melville”, “Moby Dick”, “0-553-21311-3”, 8.99, “fiction”, “J. R. R. Tolkien”, “The Lord of the Rings”, “0-395-19395-8”, 22.99, “red”, 19.95] |
Sample JSON
Here is the sample JSON used for the tests:
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}