aeson-jsonpath-0.3.0.2/0000755000000000000000000000000007346545000012774 5ustar0000000000000000aeson-jsonpath-0.3.0.2/CHANGELOG.md0000644000000000000000000000171607346545000014612 0ustar0000000000000000# Change Log All notable changes to this package are documented in this file. This project adheres to [Haskell PVP](https://pvp.haskell.org/) versioning. ## 0.3.0.2 - #43, Fix spaces not allowed in relative query and singular query segments - #50, Fix wrong normalized path with descendant segment - #30, Fix escape characters not handled properly in normalized path ## 0.3.0.1 - #31, Fix `test/cts.json` not included in hackage bundle ## 0.3.0.0 - Always return `Vector Value` - Implement Filter Selector - Introduce JSONPath Standard Compliance Testing, See: [test-suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite) - Add function `queryLocated` which also returns node locations ## 0.2.0.0 - Remove dependency on `protolude` - Fix parsing bug with Wildcard Selector - Implement Descendant Segment - Fix allowed characters in the `member-name-shorthand` - Add `QuasiQuoter` for compile-time syntax checking ## 0.1.0.0 - Initial Release aeson-jsonpath-0.3.0.2/LICENSE0000644000000000000000000000206307346545000014002 0ustar0000000000000000MIT License Copyright (c) 2024-2025 Taimoor Zaeem Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. aeson-jsonpath-0.3.0.2/README.md0000644000000000000000000000734307346545000014262 0ustar0000000000000000# aeson-jsonpath [![Build](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/build.yml/badge.svg)](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/build.yml) [![hackage-docs](https://img.shields.io/badge/hackage-v0.3.0.1-blue)](https://hackage.haskell.org/package/aeson-jsonpath) [![Donate](https://img.shields.io/badge/Donate-Patreon-red)](https://www.patreon.com/taimoorzaeem) [![Compliance](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/compliance.yml/badge.svg)](https://github.com/taimoorzaeem/aeson-jsonpath/actions/workflows/compliance.yml) Run [RFC 9535](https://www.rfc-editor.org/rfc/rfc9535) compliant JSONPath queries on [Data.Aeson](https://hackage.haskell.org/package/aeson). ## Roadmap - [x] Selectors - [x] Name Selector - [x] Index Selector - [x] Slice Selector - [x] Wildcard Selector - [x] Filter Selector - [x] Segments - [x] Child Segment - [x] Descendant Segment - [x] Normalized Paths - [ ] Function Extensions I have decided not to implement Function Extension yet. Please open an issue or discussion if you'd like to see them implemented. ## Quick Start ```haskell {-# LANGUAGE QuasiQuotes #-} import Data.Aeson (Value (..)) import Data.Aeson.QQ.Simple (aesonQQ) import Data.Aeson.JSONPath (query, queryLocated, jsonPath) track = [aesonQQ| { "artist": "Duster", "title": "Earth Moon Transit" } |] ghci> query "$.artist" track -- child member shorthand Right [String "Duster"] ghci> queryLocated "$.*" track -- child wildcard segment Right [ ("$['artist']", String "Duster"), ("$['title']", String "Earth Moon Transit") ] ``` ## More Examples ```haskell {-# LANGUAGE QuasiQuotes #-} import Data.Aeson (Value (..)) import Data.Aeson.QQ.Simple (aesonQQ) import Data.Aeson.JSONPath (query, queryLocated, jsonPath) json = [aesonQQ| { "shop": { "movies": [ { "title": "Mandy", "director": "Panos Cosmatos", "year": 2018 }, { "title": "Laurence Anyways", "director": "Xavier Dolan", "year": 2012 } ] } }|] ``` ### Child Segment ```haskell ghci> query "$.shop.movies[0].title" json Right [String "Mandy"] ghci> query "$.shop.movies[0].*" json Right [ String "Mandy", String "Panos Cosmatos", Number 2018.0 ] ghci> query "$['shop']['new-movies']" json Right [] ``` ### Descendant Segment ```haskell -- get all values with key "director", recursively ghci> query "$..director" json Right [ String "Panos Cosmatos", String "Xavier Dolan" ] ``` ### Slice Selector ```haskell ghci> query "$[2:5]" [aesonQQ| [1,2,3,4,5,6] |] Right [ Number 3.0, Number 4.0, Number 5.0 ] ``` ### Filter Selector ```haskell ghci> query "$.shop.movies[?@.year < 2015]" json Right [ Object (fromList [ ("director",String "Xavier Dolan"), ("title",String "Laurence Anyways"), ("year",Number 2012.0) ]) ] ghci> queryLocated "$.shop.movies[?@.director == 'Panos Cosmatos']" json Right [ ( "$['shop']['movies'][0]", Object (fromList [ ("director",String "Panos Cosmatos"), ("title",String "Mandy"), ("year",Number 2018.0) ]) ) ] ``` ### QuasiQuoter The functions `queryQQ` and `queryLocatedQQ` can be used with the `jsonPath` quasi quoter. ```haskell queryQQ [jsonPath|$.shop.movies|] json -- compiles successfully queryQQ [jsonPath|$.shop$$movies|] json -- compilation error, doesn't parse ``` ## Testing It is tested using 10000+ lines test suite given by [jsonpath-compliance-test-suite](https://github.com/jsonpath-standard/jsonpath-compliance-test-suite) :rocket:. **Note:** All tests pass except tests related to **function extensions** which we have not implemented yet. ## Development Please report any bugs you encounter by opening an issue. aeson-jsonpath-0.3.0.2/aeson-jsonpath.cabal0000644000000000000000000001015307346545000016711 0ustar0000000000000000name: aeson-jsonpath version: 0.3.0.2 synopsis: Parse and run JSONPath queries on Aeson documents description: RFC 9535 compliant JSONPath parsing and querying package. JSONPath is similar to XPath for querying XML documents. license: MIT license-file: LICENSE author: Taimoor Zaeem maintainer: Taimoor Zaeem category: JSON, Text, Web homepage: https://github.com/taimoorzaeem/aeson-jsonpath bug-reports: https://github.com/taimoorzaeem/aeson-jsonpath/issues build-type: Simple extra-source-files: CHANGELOG.md extra-doc-files: README.md data-files: jsonpath-compliance-test-suite/cts.json cabal-version: 1.18 tested-with: GHC == 9.4.8 , GHC == 9.6.6 , GHC == 9.8.4 , GHC == 9.10.1 source-repository head type: git location: https://github.com/taimoorzaeem/aeson-jsonpath library default-language: Haskell2010 default-extensions: OverloadedStrings NoImplicitPrelude QuasiQuotes hs-source-dirs: src exposed-modules: Data.Aeson.JSONPath Data.Aeson.JSONPath.Parser Data.Aeson.JSONPath.Query Data.Aeson.JSONPath.Types other-modules: Data.Aeson.JSONPath.Parser.Query Data.Aeson.JSONPath.Parser.Filter Data.Aeson.JSONPath.Parser.Name Data.Aeson.JSONPath.Parser.Number Data.Aeson.JSONPath.Parser.Common Data.Aeson.JSONPath.Query.Query Data.Aeson.JSONPath.Query.Segment Data.Aeson.JSONPath.Query.Selector Data.Aeson.JSONPath.Query.Filter Data.Aeson.JSONPath.Types.Query Data.Aeson.JSONPath.Types.Segment Data.Aeson.JSONPath.Types.Selector Data.Aeson.JSONPath.Types.Filter build-depends: aeson >= 2.0.3 && < 2.3 , base >= 4.9 && < 4.22 , parsec >= 3.1.11 && < 3.2 , scientific >= 0.3.4 && < 0.4 , template-haskell >= 2.12 && < 2.24 , text >= 1.2.2 && < 2.2 , vector >= 0.11 && < 0.14 ghc-options: -Wall -Wunused-packages test-suite spec type: exitcode-stdio-1.0 default-language: Haskell2010 default-extensions: OverloadedStrings NoImplicitPrelude QuasiQuotes hs-source-dirs: test/spec main-is: Main.hs other-modules: ParserSpec QuerySpec LocatedSpec build-depends: aeson >= 2.0.3 && < 2.3 , aeson-jsonpath , base >= 4.9 && < 4.22 , hspec >= 2.3 && < 2.12 , parsec >= 3.1.11 && < 3.2 , vector >= 0.11 && < 0.14 ghc-options: -Wall -Wunused-packages test-suite compliance type: exitcode-stdio-1.0 default-language: Haskell2010 default-extensions: OverloadedStrings NoImplicitPrelude QuasiQuotes hs-source-dirs: test/compliance main-is: Main.hs other-modules: ComplianceSpec Paths_aeson_jsonpath build-depends: aeson >= 2.0.3 && < 2.3 , aeson-jsonpath , base >= 4.9 && < 4.22 , hspec >= 2.3 && < 2.12 , parsec >= 3.1.11 && < 3.2 , text >= 1.2.2 && < 2.2 , vector >= 0.11 && < 0.14 ghc-options: -Wall -Wunused-packages aeson-jsonpath-0.3.0.2/jsonpath-compliance-test-suite/0000755000000000000000000000000007346545000021036 5ustar0000000000000000aeson-jsonpath-0.3.0.2/jsonpath-compliance-test-suite/cts.json0000644000000000000000000067015307346545000022536 0ustar0000000000000000{ "description": "JSONPath Compliance Test Suite. This file is autogenerated, do not edit.", "tests": [ { "name": "basic, root", "selector": "$", "document": [ "first", "second" ], "result": [ [ "first", "second" ] ], "result_paths": [ "$" ] }, { "name": "basic, no leading whitespace", "selector": " $", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "basic, no trailing whitespace", "selector": "$ ", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "basic, name shorthand", "selector": "$.a", "document": { "a": "A", "b": "B" }, "result": [ "A" ], "result_paths": [ "$['a']" ] }, { "name": "basic, name shorthand, extended unicode ☺", "selector": "$.☺", "document": { "☺": "A", "b": "B" }, "result": [ "A" ], "result_paths": [ "$['☺']" ] }, { "name": "basic, name shorthand, underscore", "selector": "$._", "document": { "_": "A", "_foo": "B" }, "result": [ "A" ], "result_paths": [ "$['_']" ] }, { "name": "basic, name shorthand, symbol", "selector": "$.&", "invalid_selector": true }, { "name": "basic, name shorthand, number", "selector": "$.1", "invalid_selector": true }, { "name": "basic, name shorthand, absent data", "selector": "$.c", "document": { "a": "A", "b": "B" }, "result": [], "result_paths": [] }, { "name": "basic, name shorthand, array data", "selector": "$.a", "document": [ "first", "second" ], "result": [], "result_paths": [] }, { "name": "basic, name shorthand, object data, nested", "selector": "$.a.b.c", "document": { "a": { "b": { "c": "C" } } }, "result": [ "C" ], "result_paths": [ "$['a']['b']['c']" ] }, { "name": "basic, wildcard shorthand, object data", "selector": "$.*", "document": { "a": "A", "b": "B" }, "results": [ [ "A", "B" ], [ "B", "A" ] ], "results_paths": [ [ "$['a']", "$['b']" ], [ "$['b']", "$['a']" ] ] }, { "name": "basic, wildcard shorthand, array data", "selector": "$.*", "document": [ "first", "second" ], "result": [ "first", "second" ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "basic, wildcard selector, array data", "selector": "$[*]", "document": [ "first", "second" ], "result": [ "first", "second" ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "basic, wildcard shorthand, then name shorthand", "selector": "$.*.a", "document": { "x": { "a": "Ax", "b": "Bx" }, "y": { "a": "Ay", "b": "By" } }, "results": [ [ "Ax", "Ay" ], [ "Ay", "Ax" ] ], "results_paths": [ [ "$['x']['a']", "$['y']['a']" ], [ "$['y']['a']", "$['x']['a']" ] ] }, { "name": "basic, multiple selectors", "selector": "$[0,2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0, 2 ], "result_paths": [ "$[0]", "$[2]" ] }, { "name": "basic, multiple selectors, space instead of comma", "selector": "$[0 2]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "basic, selector, leading comma", "selector": "$[,0]", "invalid_selector": true }, { "name": "basic, selector, trailing comma", "selector": "$[0,]", "invalid_selector": true }, { "name": "basic, multiple selectors, name and index, array data", "selector": "$['a',1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1 ], "result_paths": [ "$[1]" ] }, { "name": "basic, multiple selectors, name and index, object data", "selector": "$['a',1]", "document": { "a": 1, "b": 2 }, "result": [ 1 ], "result_paths": [ "$['a']" ] }, { "name": "basic, multiple selectors, index and slice", "selector": "$[1,5:7]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1, 5, 6 ], "result_paths": [ "$[1]", "$[5]", "$[6]" ] }, { "name": "basic, multiple selectors, index and slice, overlapping", "selector": "$[1,0:3]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1, 0, 1, 2 ], "result_paths": [ "$[1]", "$[0]", "$[1]", "$[2]" ] }, { "name": "basic, multiple selectors, duplicate index", "selector": "$[1,1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1, 1 ], "result_paths": [ "$[1]", "$[1]" ] }, { "name": "basic, multiple selectors, wildcard and index", "selector": "$[*,1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1 ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]", "$[4]", "$[5]", "$[6]", "$[7]", "$[8]", "$[9]", "$[1]" ] }, { "name": "basic, multiple selectors, wildcard and name", "selector": "$[*,'a']", "document": { "a": "A", "b": "B" }, "results": [ [ "A", "B", "A" ], [ "B", "A", "A" ] ], "results_paths": [ [ "$['a']", "$['b']", "$['a']" ], [ "$['b']", "$['a']", "$['a']" ] ] }, { "name": "basic, multiple selectors, wildcard and slice", "selector": "$[*,0:2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]", "$[4]", "$[5]", "$[6]", "$[7]", "$[8]", "$[9]", "$[0]", "$[1]" ] }, { "name": "basic, multiple selectors, multiple wildcards", "selector": "$[*,*]", "document": [ 0, 1, 2 ], "result": [ 0, 1, 2, 0, 1, 2 ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[0]", "$[1]", "$[2]" ] }, { "name": "basic, empty segment", "selector": "$[]", "invalid_selector": true }, { "name": "basic, descendant segment, index", "selector": "$..[1]", "document": { "o": [ 0, 1, [ 2, 3 ] ] }, "result": [ 1, 3 ], "result_paths": [ "$['o'][1]", "$['o'][2][1]" ] }, { "name": "basic, descendant segment, name shorthand", "selector": "$..a", "document": { "o": [ { "a": "b" }, { "a": "c" } ] }, "result": [ "b", "c" ], "result_paths": [ "$['o'][0]['a']", "$['o'][1]['a']" ] }, { "name": "basic, descendant segment, wildcard shorthand, array data", "selector": "$..*", "document": [ 0, 1 ], "result": [ 0, 1 ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "basic, descendant segment, wildcard selector, array data", "selector": "$..[*]", "document": [ 0, 1 ], "result": [ 0, 1 ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "basic, descendant segment, wildcard selector, nested arrays", "selector": "$..[*]", "document": [ [ [ 1 ] ], [ 2 ] ], "results": [ [ [ [ 1 ] ], [ 2 ], [ 1 ], 1, 2 ], [ [ [ 1 ] ], [ 2 ], [ 1 ], 2, 1 ] ], "results_paths": [ [ "$[0]", "$[1]", "$[0][0]", "$[0][0][0]", "$[1][0]" ], [ "$[0]", "$[1]", "$[0][0]", "$[1][0]", "$[0][0][0]" ] ] }, { "name": "basic, descendant segment, wildcard selector, nested objects", "selector": "$..[*]", "document": { "a": { "c": { "e": 1 } }, "b": { "d": 2 } }, "results": [ [ { "c": { "e": 1 } }, { "d": 2 }, { "e": 1 }, 1, 2 ], [ { "c": { "e": 1 } }, { "d": 2 }, { "e": 1 }, 2, 1 ], [ { "c": { "e": 1 } }, { "d": 2 }, 2, { "e": 1 }, 1 ], [ { "d": 2 }, { "c": { "e": 1 } }, { "e": 1 }, 1, 2 ], [ { "d": 2 }, { "c": { "e": 1 } }, { "e": 1 }, 2, 1 ], [ { "d": 2 }, { "c": { "e": 1 } }, 2, { "e": 1 }, 1 ] ], "results_paths": [ [ "$['a']", "$['b']", "$['a']['c']", "$['a']['c']['e']", "$['b']['d']" ], [ "$['a']", "$['b']", "$['a']['c']", "$['b']['d']", "$['a']['c']['e']" ], [ "$['a']", "$['b']", "$['b']['d']", "$['a']['c']", "$['a']['c']['e']" ], [ "$['b']", "$['a']", "$['a']['c']", "$['a']['c']['e']", "$['b']['d']" ], [ "$['b']", "$['a']", "$['a']['c']", "$['b']['d']", "$['a']['c']['e']" ], [ "$['b']", "$['a']", "$['b']['d']", "$['a']['c']", "$['a']['c']['e']" ] ] }, { "name": "basic, descendant segment, wildcard shorthand, object data", "selector": "$..*", "document": { "a": "b" }, "result": [ "b" ], "result_paths": [ "$['a']" ] }, { "name": "basic, descendant segment, wildcard shorthand, nested data", "selector": "$..*", "document": { "o": [ { "a": "b" } ] }, "result": [ [ { "a": "b" } ], { "a": "b" }, "b" ], "result_paths": [ "$['o']", "$['o'][0]", "$['o'][0]['a']" ] }, { "name": "basic, descendant segment, multiple selectors", "selector": "$..['a','d']", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ "b", "e", "c", "f" ], "result_paths": [ "$[0]['a']", "$[0]['d']", "$[1]['a']", "$[1]['d']" ] }, { "name": "basic, descendant segment, object traversal, multiple selectors", "selector": "$..['a','d']", "document": { "x": { "a": "b", "d": "e" }, "y": { "a": "c", "d": "f" } }, "results": [ [ "b", "e", "c", "f" ], [ "c", "f", "b", "e" ] ], "results_paths": [ [ "$['x']['a']", "$['x']['d']", "$['y']['a']", "$['y']['d']" ], [ "$['y']['a']", "$['y']['d']", "$['x']['a']", "$['x']['d']" ] ] }, { "name": "basic, bald descendant segment", "selector": "$..", "invalid_selector": true }, { "name": "basic, current node identifier without filter selector", "selector": "$[@.a]", "invalid_selector": true }, { "name": "basic, root node identifier in brackets without filter selector", "selector": "$[$.a]", "invalid_selector": true }, { "name": "filter, existence, without segments", "selector": "$[?@]", "document": { "a": 1, "b": null }, "results": [ [ 1, null ], [ null, 1 ] ], "results_paths": [ [ "$['a']", "$['b']" ], [ "$['b']", "$['a']" ] ] }, { "name": "filter, existence", "selector": "$[?@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, existence, present with null", "selector": "$[?@.a]", "document": [ { "a": null, "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": null, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, absolute existence, without segments", "selector": "$[?$]", "document": { "a": 1, "b": null }, "results": [ [ 1, null ], [ null, 1 ] ], "results_paths": [ [ "$['a']", "$['b']" ], [ "$['b']", "$['a']" ] ] }, { "name": "filter, absolute existence, with segments", "selector": "$[?$.*.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "filter, equals string, single quotes", "selector": "$[?@.a=='b']", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals numeric string, single quotes", "selector": "$[?@.a=='1']", "document": [ { "a": "1", "d": "e" }, { "a": 1, "d": "f" } ], "result": [ { "a": "1", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals string, double quotes", "selector": "$[?@.a==\"b\"]", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals numeric string, double quotes", "selector": "$[?@.a==\"1\"]", "document": [ { "a": "1", "d": "e" }, { "a": 1, "d": "f" } ], "result": [ { "a": "1", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number", "selector": "$[?@.a==1]", "document": [ { "a": 1, "d": "e" }, { "a": "c", "d": "f" }, { "a": 2, "d": "f" }, { "a": "1", "d": "f" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals null", "selector": "$[?@.a==null]", "document": [ { "a": null, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": null, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals null, absent from data", "selector": "$[?@.a==null]", "document": [ { "d": "e" }, { "a": "c", "d": "f" } ], "result": [], "result_paths": [] }, { "name": "filter, equals true", "selector": "$[?@.a==true]", "document": [ { "a": true, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": true, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals false", "selector": "$[?@.a==false]", "document": [ { "a": false, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": false, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals self", "selector": "$[?@==@]", "document": [ 1, null, true, { "a": "b" }, [ false ] ], "result": [ 1, null, true, { "a": "b" }, [ false ] ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]", "$[4]" ] }, { "name": "filter, absolute, equals self", "selector": "$[?$==$]", "document": [ 1, null, true, { "a": "b" }, [ false ] ], "result": [ 1, null, true, { "a": "b" }, [ false ] ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]", "$[4]" ] }, { "name": "filter, equals, absent from index selector equals absent from name selector", "selector": "$[?@.absent==@.list[9]]", "document": [ { "list": [ 1 ] } ], "result": [ { "list": [ 1 ] } ], "result_paths": [ "$[0]" ] }, { "name": "filter, deep equality, arrays", "selector": "$[?@.a==@.b]", "document": [ { "a": false, "b": [ 1, 2 ] }, { "a": [ [ 1, [ 2 ] ] ], "b": [ [ 1, [ 2 ] ] ] }, { "a": [ [ 1, [ 2 ] ] ], "b": [ [ [ 2 ], 1 ] ] }, { "a": [ [ 1, [ 2 ] ] ], "b": [ [ 1, 2 ] ] } ], "result": [ { "a": [ [ 1, [ 2 ] ] ], "b": [ [ 1, [ 2 ] ] ] } ], "result_paths": [ "$[1]" ] }, { "name": "filter, deep equality, objects", "selector": "$[?@.a==@.b]", "document": [ { "a": false, "b": { "x": 1, "y": { "z": 1 } } }, { "a": { "x": 1, "y": { "z": 1 } }, "b": { "x": 1, "y": { "z": 1 } } }, { "a": { "x": 1, "y": { "z": 1 } }, "b": { "y": { "z": 1 }, "x": 1 } }, { "a": { "x": 1, "y": { "z": 1 } }, "b": { "x": 1 } }, { "a": { "x": 1, "y": { "z": 1 } }, "b": { "x": 1, "y": { "z": 2 } } } ], "result": [ { "a": { "x": 1, "y": { "z": 1 } }, "b": { "x": 1, "y": { "z": 1 } } }, { "a": { "x": 1, "y": { "z": 1 } }, "b": { "y": { "z": 1 }, "x": 1 } } ], "result_paths": [ "$[1]", "$[2]" ] }, { "name": "filter, not-equals string, single quotes", "selector": "$[?@.a!='b']", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals numeric string, single quotes", "selector": "$[?@.a!='1']", "document": [ { "a": "1", "d": "e" }, { "a": 1, "d": "f" } ], "result": [ { "a": 1, "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals string, single quotes, different type", "selector": "$[?@.a!='b']", "document": [ { "a": "b", "d": "e" }, { "a": 1, "d": "f" } ], "result": [ { "a": 1, "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals string, double quotes", "selector": "$[?@.a!=\"b\"]", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals numeric string, double quotes", "selector": "$[?@.a!=\"1\"]", "document": [ { "a": "1", "d": "e" }, { "a": 1, "d": "f" } ], "result": [ { "a": 1, "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals string, double quotes, different types", "selector": "$[?@.a!=\"b\"]", "document": [ { "a": "b", "d": "e" }, { "a": 1, "d": "f" } ], "result": [ { "a": 1, "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals number", "selector": "$[?@.a!=1]", "document": [ { "a": 1, "d": "e" }, { "a": 2, "d": "f" }, { "a": "1", "d": "f" } ], "result": [ { "a": 2, "d": "f" }, { "a": "1", "d": "f" } ], "result_paths": [ "$[1]", "$[2]" ] }, { "name": "filter, not-equals number, different types", "selector": "$[?@.a!=1]", "document": [ { "a": 1, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals null", "selector": "$[?@.a!=null]", "document": [ { "a": null, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals null, absent from data", "selector": "$[?@.a!=null]", "document": [ { "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "d": "e" }, { "a": "c", "d": "f" } ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "filter, not-equals true", "selector": "$[?@.a!=true]", "document": [ { "a": true, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not-equals false", "selector": "$[?@.a!=false]", "document": [ { "a": false, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, less than string, single quotes", "selector": "$[?@.a<'c']", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, less than string, double quotes", "selector": "$[?@.a<\"c\"]", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, less than number", "selector": "$[?@.a<10]", "document": [ { "a": 1, "d": "e" }, { "a": 10, "d": "e" }, { "a": "c", "d": "f" }, { "a": 20, "d": "f" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, less than null", "selector": "$[?@.a'c']", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "d", "d": "f" } ], "result_paths": [ "$[2]" ] }, { "name": "filter, greater than string, double quotes", "selector": "$[?@.a>\"c\"]", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "d", "d": "f" } ], "result_paths": [ "$[2]" ] }, { "name": "filter, greater than number", "selector": "$[?@.a>10]", "document": [ { "a": 1, "d": "e" }, { "a": 10, "d": "e" }, { "a": "c", "d": "f" }, { "a": 20, "d": "f" } ], "result": [ { "a": 20, "d": "f" } ], "result_paths": [ "$[3]" ] }, { "name": "filter, greater than null", "selector": "$[?@.a>null]", "document": [ { "a": null, "d": "e" }, { "a": "c", "d": "f" } ], "result": [], "result_paths": [] }, { "name": "filter, greater than true", "selector": "$[?@.a>true]", "document": [ { "a": true, "d": "e" }, { "a": "c", "d": "f" } ], "result": [], "result_paths": [] }, { "name": "filter, greater than false", "selector": "$[?@.a>false]", "document": [ { "a": false, "d": "e" }, { "a": "c", "d": "f" } ], "result": [], "result_paths": [] }, { "name": "filter, greater than or equal to string, single quotes", "selector": "$[?@.a>='c']", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[1]", "$[2]" ] }, { "name": "filter, greater than or equal to string, double quotes", "selector": "$[?@.a>=\"c\"]", "document": [ { "a": "b", "d": "e" }, { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[1]", "$[2]" ] }, { "name": "filter, greater than or equal to number", "selector": "$[?@.a>=10]", "document": [ { "a": 1, "d": "e" }, { "a": 10, "d": "e" }, { "a": "c", "d": "f" }, { "a": 20, "d": "f" } ], "result": [ { "a": 10, "d": "e" }, { "a": 20, "d": "f" } ], "result_paths": [ "$[1]", "$[3]" ] }, { "name": "filter, greater than or equal to null", "selector": "$[?@.a>=null]", "document": [ { "a": null, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": null, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, greater than or equal to true", "selector": "$[?@.a>=true]", "document": [ { "a": true, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": true, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, greater than or equal to false", "selector": "$[?@.a>=false]", "document": [ { "a": false, "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": false, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, exists and not-equals null, absent from data", "selector": "$[?@.a&&@.a!=null]", "document": [ { "d": "e" }, { "a": "c", "d": "f" } ], "result": [ { "a": "c", "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, exists and exists, data false", "selector": "$[?@.a&&@.b]", "document": [ { "a": false, "b": false }, { "b": false }, { "c": false } ], "result": [ { "a": false, "b": false } ], "result_paths": [ "$[0]" ] }, { "name": "filter, exists or exists, data false", "selector": "$[?@.a||@.b]", "document": [ { "a": false, "b": false }, { "b": false }, { "c": false } ], "result": [ { "a": false, "b": false }, { "b": false } ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "filter, and", "selector": "$[?@.a>0&&@.a<10]", "document": [ { "a": -10, "d": "e" }, { "a": 5, "d": "f" }, { "a": 20, "d": "f" } ], "result": [ { "a": 5, "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, or", "selector": "$[?@.a=='b'||@.a=='d']", "document": [ { "a": "a", "d": "e" }, { "a": "b", "d": "f" }, { "a": "c", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "b", "d": "f" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[1]", "$[3]" ] }, { "name": "filter, not expression", "selector": "$[?!(@.a=='b')]", "document": [ { "a": "a", "d": "e" }, { "a": "b", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "a", "d": "e" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[0]", "$[2]" ] }, { "name": "filter, not exists", "selector": "$[?!@.a]", "document": [ { "a": "a", "d": "e" }, { "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, not exists, data null", "selector": "$[?!@.a]", "document": [ { "a": null, "d": "e" }, { "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "d": "f" } ], "result_paths": [ "$[1]" ] }, { "name": "filter, non-singular existence, wildcard", "selector": "$[?@.*]", "document": [ 1, [], [ 2 ], {}, { "a": 3 } ], "result": [ [ 2 ], { "a": 3 } ], "result_paths": [ "$[2]", "$[4]" ] }, { "name": "filter, non-singular existence, multiple", "selector": "$[?@[0, 0, 'a']]", "document": [ 1, [], [ 2 ], [ 2, 3 ], { "a": 3 }, { "b": 4 }, { "a": 3, "b": 4 } ], "result": [ [ 2 ], [ 2, 3 ], { "a": 3 }, { "a": 3, "b": 4 } ], "result_paths": [ "$[2]", "$[3]", "$[4]", "$[6]" ] }, { "name": "filter, non-singular existence, slice", "selector": "$[?@[0:2]]", "document": [ 1, [], [ 2 ], [ 2, 3, 4 ], {}, { "a": 3 } ], "result": [ [ 2 ], [ 2, 3, 4 ] ], "result_paths": [ "$[2]", "$[3]" ] }, { "name": "filter, non-singular existence, negated", "selector": "$[?!@.*]", "document": [ 1, [], [ 2 ], {}, { "a": 3 } ], "result": [ 1, [], {} ], "result_paths": [ "$[0]", "$[1]", "$[3]" ] }, { "name": "filter, non-singular query in comparison, slice", "selector": "$[?@[0:0]==0]", "invalid_selector": true }, { "name": "filter, non-singular query in comparison, all children", "selector": "$[?@[*]==0]", "invalid_selector": true }, { "name": "filter, non-singular query in comparison, descendants", "selector": "$[?@..a==0]", "invalid_selector": true }, { "name": "filter, non-singular query in comparison, combined", "selector": "$[?@.a[*].a==0]", "invalid_selector": true }, { "name": "filter, nested", "selector": "$[?@[?@>1]]", "document": [ [ 0 ], [ 0, 1 ], [ 0, 1, 2 ], [ 42 ] ], "result": [ [ 0, 1, 2 ], [ 42 ] ], "result_paths": [ "$[2]", "$[3]" ] }, { "name": "filter, name segment on primitive, selects nothing", "selector": "$[?@.a == 1]", "document": { "a": 1 }, "result": [], "result_paths": [] }, { "name": "filter, name segment on array, selects nothing", "selector": "$[?@['0'] == 5]", "document": [ [ 5, 6 ] ], "result": [], "result_paths": [] }, { "name": "filter, index segment on object, selects nothing", "selector": "$[?@[0] == 5]", "document": [ { "0": 5 } ], "result": [], "result_paths": [] }, { "name": "filter, followed by name selector", "selector": "$[?@.a==1].b.x", "document": [ { "a": 1, "b": { "x": 2 } } ], "result": [ 2 ], "result_paths": [ "$[0]['b']['x']" ] }, { "name": "filter, followed by child segment that selects multiple elements", "selector": "$[?@.z=='_']['x','y']", "document": [ { "x": 1, "y": null, "z": "_" } ], "result": [ 1, null ], "result_paths": [ "$[0]['x']", "$[0]['y']" ] }, { "name": "filter, relative non-singular query, index, equal", "selector": "$[?(@[0, 0]==42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, index, not equal", "selector": "$[?(@[0, 0]!=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, index, less-or-equal", "selector": "$[?(@[0, 0]<=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, name, equal", "selector": "$[?(@['a', 'a']==42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, name, not equal", "selector": "$[?(@['a', 'a']!=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, name, less-or-equal", "selector": "$[?(@['a', 'a']<=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, combined, equal", "selector": "$[?(@[0, '0']==42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, combined, not equal", "selector": "$[?(@[0, '0']!=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, combined, less-or-equal", "selector": "$[?(@[0, '0']<=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, relative non-singular query, wildcard, equal", "selector": "$[?(@.*==42)]", "invalid_selector": true }, { "name": "filter, relative non-singular query, wildcard, not equal", "selector": "$[?(@.*!=42)]", "invalid_selector": true }, { "name": "filter, relative non-singular query, wildcard, less-or-equal", "selector": "$[?(@.*<=42)]", "invalid_selector": true }, { "name": "filter, relative non-singular query, slice, equal", "selector": "$[?(@[0:0]==42)]", "invalid_selector": true }, { "name": "filter, relative non-singular query, slice, not equal", "selector": "$[?(@[0:0]!=42)]", "invalid_selector": true }, { "name": "filter, relative non-singular query, slice, less-or-equal", "selector": "$[?(@[0:0]<=42)]", "invalid_selector": true }, { "name": "filter, absolute non-singular query, index, equal", "selector": "$[?($[0, 0]==42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, index, not equal", "selector": "$[?($[0, 0]!=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, index, less-or-equal", "selector": "$[?($[0, 0]<=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, name, equal", "selector": "$[?($['a', 'a']==42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, name, not equal", "selector": "$[?($['a', 'a']!=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, name, less-or-equal", "selector": "$[?($['a', 'a']<=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, combined, equal", "selector": "$[?($[0, '0']==42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, combined, not equal", "selector": "$[?($[0, '0']!=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, combined, less-or-equal", "selector": "$[?($[0, '0']<=42)]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, absolute non-singular query, wildcard, equal", "selector": "$[?($.*==42)]", "invalid_selector": true }, { "name": "filter, absolute non-singular query, wildcard, not equal", "selector": "$[?($.*!=42)]", "invalid_selector": true }, { "name": "filter, absolute non-singular query, wildcard, less-or-equal", "selector": "$[?($.*<=42)]", "invalid_selector": true }, { "name": "filter, absolute non-singular query, slice, equal", "selector": "$[?($[0:0]==42)]", "invalid_selector": true }, { "name": "filter, absolute non-singular query, slice, not equal", "selector": "$[?($[0:0]!=42)]", "invalid_selector": true }, { "name": "filter, absolute non-singular query, slice, less-or-equal", "selector": "$[?($[0:0]<=42)]", "invalid_selector": true }, { "name": "filter, multiple selectors", "selector": "$[?@.a,?@.b]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "filter, multiple selectors, comparison", "selector": "$[?@.a=='b',?@.b=='x']", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, multiple selectors, overlapping", "selector": "$[?@.a,?@.d]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" }, { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result_paths": [ "$[0]", "$[0]", "$[1]" ] }, { "name": "filter, multiple selectors, filter and index", "selector": "$[?@.a,1]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result_paths": [ "$[0]", "$[1]" ] }, { "name": "filter, multiple selectors, filter and wildcard", "selector": "$[?@.a,*]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" }, { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result_paths": [ "$[0]", "$[0]", "$[1]" ] }, { "name": "filter, multiple selectors, filter and slice", "selector": "$[?@.a,1:]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" }, { "g": "h" } ], "result": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" }, { "g": "h" } ], "result_paths": [ "$[0]", "$[1]", "$[2]" ] }, { "name": "filter, multiple selectors, comparison filter, index and slice", "selector": "$[1, ?@.a=='b', 1:]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "b": "c", "d": "f" }, { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result_paths": [ "$[1]", "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "filter, equals number, zero and negative zero", "selector": "$[?@.a==0]", "document": [ { "a": 0, "d": "e" }, { "a": 0.1, "d": "f" }, { "a": "0", "d": "g" } ], "result": [ { "a": 0, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, negative zero and zero", "selector": "$[?@.a==-0]", "document": [ { "a": 0, "d": "e" }, { "a": 0.1, "d": "f" }, { "a": "0", "d": "g" } ], "result": [ { "a": 0, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, with and without decimal fraction", "selector": "$[?@.a==1.0]", "document": [ { "a": 1, "d": "e" }, { "a": 2, "d": "f" }, { "a": "1", "d": "g" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent", "selector": "$[?@.a==1e2]", "document": [ { "a": 100, "d": "e" }, { "a": 100.1, "d": "f" }, { "a": "100", "d": "g" } ], "result": [ { "a": 100, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent upper e", "selector": "$[?@.a==1E2]", "document": [ { "a": 100, "d": "e" }, { "a": 100.1, "d": "f" }, { "a": "100", "d": "g" } ], "result": [ { "a": 100, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, positive exponent", "selector": "$[?@.a==1e+2]", "document": [ { "a": 100, "d": "e" }, { "a": 100.1, "d": "f" }, { "a": "100", "d": "g" } ], "result": [ { "a": 100, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, negative exponent", "selector": "$[?@.a==1e-2]", "document": [ { "a": 0.01, "d": "e" }, { "a": 0.02, "d": "f" }, { "a": "0.01", "d": "g" } ], "result": [ { "a": 0.01, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent 0", "selector": "$[?@.a==1e0]", "document": [ { "a": 1, "d": "e" }, { "a": 2, "d": "f" }, { "a": "1", "d": "g" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent -0", "selector": "$[?@.a==1e-0]", "document": [ { "a": 1, "d": "e" }, { "a": 2, "d": "f" }, { "a": "1", "d": "g" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent +0", "selector": "$[?@.a==1e+0]", "document": [ { "a": 1, "d": "e" }, { "a": 2, "d": "f" }, { "a": "1", "d": "g" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent leading -0", "selector": "$[?@.a==1e-02]", "document": [ { "a": 0.01, "d": "e" }, { "a": 0.02, "d": "f" }, { "a": "0.01", "d": "g" } ], "result": [ { "a": 0.01, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, exponent +00", "selector": "$[?@.a==1e+00]", "document": [ { "a": 1, "d": "e" }, { "a": 2, "d": "f" }, { "a": "1", "d": "g" } ], "result": [ { "a": 1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, decimal fraction", "selector": "$[?@.a==1.1]", "document": [ { "a": 1.1, "d": "e" }, { "a": 1, "d": "f" }, { "a": "1.1", "d": "g" } ], "result": [ { "a": 1.1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, decimal fraction, trailing 0", "selector": "$[?@.a==1.10]", "document": [ { "a": 1.1, "d": "e" }, { "a": 1, "d": "f" }, { "a": "1.1", "d": "g" } ], "result": [ { "a": 1.1, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, decimal fraction, exponent", "selector": "$[?@.a==1.1e2]", "document": [ { "a": 110, "d": "e" }, { "a": 110.1, "d": "f" }, { "a": "110", "d": "g" } ], "result": [ { "a": 110, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, decimal fraction, positive exponent", "selector": "$[?@.a==1.1e+2]", "document": [ { "a": 110, "d": "e" }, { "a": 110.1, "d": "f" }, { "a": "110", "d": "g" } ], "result": [ { "a": 110, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, decimal fraction, negative exponent", "selector": "$[?@.a==1.1e-2]", "document": [ { "a": 0.011, "d": "e" }, { "a": 0.012, "d": "f" }, { "a": "0.011", "d": "g" } ], "result": [ { "a": 0.011, "d": "e" } ], "result_paths": [ "$[0]" ] }, { "name": "filter, equals number, invalid plus", "selector": "$[?@.a==+1]", "invalid_selector": true }, { "name": "filter, equals number, invalid minus space", "selector": "$[?@.a==- 1]", "invalid_selector": true }, { "name": "filter, equals number, invalid double minus", "selector": "$[?@.a==--1]", "invalid_selector": true }, { "name": "filter, equals number, invalid no int digit", "selector": "$[?@.a==.1]", "invalid_selector": true }, { "name": "filter, equals number, invalid minus no int digit", "selector": "$[?@.a==-.1]", "invalid_selector": true }, { "name": "filter, equals number, invalid 00", "selector": "$[?@.a==00]", "invalid_selector": true }, { "name": "filter, equals number, invalid leading 0", "selector": "$[?@.a==01]", "invalid_selector": true }, { "name": "filter, equals number, invalid no fractional digit", "selector": "$[?@.a==1.]", "invalid_selector": true }, { "name": "filter, equals number, invalid middle minus", "selector": "$[?@.a==1.-1]", "invalid_selector": true }, { "name": "filter, equals number, invalid no fractional digit e", "selector": "$[?@.a==1.e1]", "invalid_selector": true }, { "name": "filter, equals number, invalid no e digit", "selector": "$[?@.a==1e]", "invalid_selector": true }, { "name": "filter, equals number, invalid no e digit minus", "selector": "$[?@.a==1e-]", "invalid_selector": true }, { "name": "filter, equals number, invalid double e", "selector": "$[?@.a==1eE1]", "invalid_selector": true }, { "name": "filter, equals number, invalid e digit double minus", "selector": "$[?@.a==1e--1]", "invalid_selector": true }, { "name": "filter, equals number, invalid e digit plus minus", "selector": "$[?@.a==1e+-1]", "invalid_selector": true }, { "name": "filter, equals number, invalid e decimal", "selector": "$[?@.a==1e2.3]", "invalid_selector": true }, { "name": "filter, equals number, invalid multi e", "selector": "$[?@.a==1e2e3]", "invalid_selector": true }, { "name": "filter, equals, special nothing", "selector": "$.values[?length(@.a) == value($..c)]", "document": { "c": "cd", "values": [ { "a": "ab" }, { "c": "d" }, { "a": null } ] }, "result": [ { "c": "d" }, { "a": null } ], "result_paths": [ "$['values'][1]", "$['values'][2]" ], "tags": [ "function" ] }, { "name": "filter, equals, empty node list and empty node list", "selector": "$[?@.a == @.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "c": 3 } ], "result_paths": [ "$[2]" ] }, { "name": "filter, equals, empty node list and special nothing", "selector": "$[?@.a == length(@.b)]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "b": 2 }, { "c": 3 } ], "result_paths": [ "$[1]", "$[2]" ], "tags": [ "function", "whitespace" ] }, { "name": "filter, object data", "selector": "$[?@<3]", "document": { "a": 1, "b": 2, "c": 3 }, "results": [ [ 1, 2 ], [ 2, 1 ] ], "results_paths": [ [ "$['a']", "$['b']" ], [ "$['b']", "$['a']" ] ] }, { "name": "filter, and binds more tightly than or", "selector": "$[?@.a || @.b && @.c]", "document": [ { "a": 1 }, { "b": 2, "c": 3 }, { "c": 3 }, { "b": 2 }, { "a": 1, "b": 2, "c": 3 } ], "result": [ { "a": 1 }, { "b": 2, "c": 3 }, { "a": 1, "b": 2, "c": 3 } ], "result_paths": [ "$[0]", "$[1]", "$[4]" ], "tags": [ "whitespace" ] }, { "name": "filter, left to right evaluation", "selector": "$[?@.a && @.b || @.c]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 }, { "a": 1, "c": 3 }, { "b": 1, "c": 3 }, { "c": 3 }, { "a": 1, "b": 2, "c": 3 } ], "result": [ { "a": 1, "b": 2 }, { "a": 1, "c": 3 }, { "b": 1, "c": 3 }, { "c": 3 }, { "a": 1, "b": 2, "c": 3 } ], "result_paths": [ "$[2]", "$[3]", "$[4]", "$[5]", "$[6]" ], "tags": [ "whitespace" ] }, { "name": "filter, group terms, left", "selector": "$[?(@.a || @.b) && @.c]", "document": [ { "a": 1, "b": 2 }, { "a": 1, "c": 3 }, { "b": 2, "c": 3 }, { "a": 1 }, { "b": 2 }, { "c": 3 }, { "a": 1, "b": 2, "c": 3 } ], "result": [ { "a": 1, "c": 3 }, { "b": 2, "c": 3 }, { "a": 1, "b": 2, "c": 3 } ], "result_paths": [ "$[1]", "$[2]", "$[6]" ], "tags": [ "whitespace" ] }, { "name": "filter, group terms, right", "selector": "$[?@.a && (@.b || @.c)]", "document": [ { "a": 1 }, { "a": 1, "b": 2 }, { "a": 1, "c": 2 }, { "b": 2 }, { "c": 2 }, { "a": 1, "b": 2, "c": 3 } ], "result": [ { "a": 1, "b": 2 }, { "a": 1, "c": 2 }, { "a": 1, "b": 2, "c": 3 } ], "result_paths": [ "$[1]", "$[2]", "$[5]" ], "tags": [ "whitespace" ] }, { "name": "filter, string literal, single quote in double quotes", "selector": "$[?@ == \"quoted' literal\"]", "document": [ "quoted' literal", "a", "quoted\\' literal" ], "result": [ "quoted' literal" ], "result_paths": [ "$[0]" ] }, { "name": "filter, string literal, double quote in single quotes", "selector": "$[?@ == 'quoted\" literal']", "document": [ "quoted\" literal", "a", "quoted\\\" literal", "'quoted\" literal'" ], "result": [ "quoted\" literal" ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "filter, string literal, escaped single quote in single quotes", "selector": "$[?@ == 'quoted\\' literal']", "document": [ "quoted' literal", "a", "quoted\\' literal", "'quoted\" literal'" ], "result": [ "quoted' literal" ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "filter, string literal, escaped double quote in double quotes", "selector": "$[?@ == \"quoted\\\" literal\"]", "document": [ "quoted\" literal", "a", "quoted\\\" literal", "'quoted\" literal'" ], "result": [ "quoted\" literal" ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "filter, literal true must be compared", "selector": "$[?true]", "invalid_selector": true }, { "name": "filter, literal false must be compared", "selector": "$[?false]", "invalid_selector": true }, { "name": "filter, literal string must be compared", "selector": "$[?'abc']", "invalid_selector": true }, { "name": "filter, literal int must be compared", "selector": "$[?2]", "invalid_selector": true }, { "name": "filter, literal float must be compared", "selector": "$[?2.2]", "invalid_selector": true }, { "name": "filter, literal null must be compared", "selector": "$[?null]", "invalid_selector": true }, { "name": "filter, and, literals must be compared", "selector": "$[?true && false]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, or, literals must be compared", "selector": "$[?true || false]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, and, right hand literal must be compared", "selector": "$[?true == false && false]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, or, right hand literal must be compared", "selector": "$[?true == false || false]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, and, left hand literal must be compared", "selector": "$[?false && true == false]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, or, left hand literal must be compared", "selector": "$[?false || true == false]", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "filter, true, incorrectly capitalized", "selector": "$[?@==True]", "invalid_selector": true, "tags": [ "case" ] }, { "name": "filter, false, incorrectly capitalized", "selector": "$[?@==False]", "invalid_selector": true, "tags": [ "case" ] }, { "name": "filter, null, incorrectly capitalized", "selector": "$[?@==Null]", "invalid_selector": true, "tags": [ "case" ] }, { "name": "index selector, first element", "selector": "$[0]", "document": [ "first", "second" ], "result": [ "first" ], "result_paths": [ "$[0]" ], "tags": [ "index" ] }, { "name": "index selector, second element", "selector": "$[1]", "document": [ "first", "second" ], "result": [ "second" ], "result_paths": [ "$[1]" ], "tags": [ "index" ] }, { "name": "index selector, out of bound", "selector": "$[2]", "document": [ "first", "second" ], "result": [], "result_paths": [], "tags": [ "boundary", "index" ] }, { "name": "index selector, min exact index", "selector": "$[-9007199254740991]", "document": [ "first", "second" ], "result": [], "result_paths": [], "tags": [ "boundary", "index" ] }, { "name": "index selector, max exact index", "selector": "$[9007199254740991]", "document": [ "first", "second" ], "result": [], "result_paths": [], "tags": [ "boundary", "index" ] }, { "name": "index selector, min exact index - 1", "selector": "$[-9007199254740992]", "invalid_selector": true, "tags": [ "boundary", "index" ] }, { "name": "index selector, max exact index + 1", "selector": "$[9007199254740992]", "invalid_selector": true, "tags": [ "boundary", "index" ] }, { "name": "index selector, overflowing index", "selector": "$[231584178474632390847141970017375815706539969331281128078915168015826259279872]", "invalid_selector": true, "tags": [ "boundary", "index" ] }, { "name": "index selector, not actually an index, overflowing index leads into general text", "selector": "$[231584178474632390847141970017375815706539969331281128078915168SomeRandomText]", "invalid_selector": true, "tags": [ "index" ] }, { "name": "index selector, negative", "selector": "$[-1]", "document": [ "first", "second" ], "result": [ "second" ], "result_paths": [ "$[1]" ], "tags": [ "index" ] }, { "name": "index selector, more negative", "selector": "$[-2]", "document": [ "first", "second" ], "result": [ "first" ], "result_paths": [ "$[0]" ], "tags": [ "index" ] }, { "name": "index selector, negative out of bound", "selector": "$[-3]", "document": [ "first", "second" ], "result": [], "result_paths": [], "tags": [ "boundary", "index" ] }, { "name": "index selector, on object", "selector": "$[0]", "document": { "foo": 1 }, "result": [], "result_paths": [], "tags": [ "index" ] }, { "name": "index selector, leading 0", "selector": "$[01]", "invalid_selector": true, "tags": [ "index" ] }, { "name": "index selector, decimal", "selector": "$[1.0]", "invalid_selector": true, "tags": [ "index" ] }, { "name": "index selector, plus", "selector": "$[+1]", "invalid_selector": true, "tags": [ "index" ] }, { "name": "index selector, minus space", "selector": "$[- 1]", "invalid_selector": true, "tags": [ "index", "whitespace" ] }, { "name": "index selector, -0", "selector": "$[-0]", "invalid_selector": true, "tags": [ "index" ] }, { "name": "index selector, leading -0", "selector": "$[-01]", "invalid_selector": true, "tags": [ "index" ] }, { "name": "name selector, double quotes", "selector": "$[\"a\"]", "document": { "a": "A", "b": "B" }, "result": [ "A" ], "result_paths": [ "$['a']" ] }, { "name": "name selector, double quotes, absent data", "selector": "$[\"c\"]", "document": { "a": "A", "b": "B" }, "result": [], "result_paths": [] }, { "name": "name selector, double quotes, array data", "selector": "$[\"a\"]", "document": [ "first", "second" ], "result": [], "result_paths": [] }, { "name": "name selector, name, double quotes, contains single quote", "selector": "$[\"a'\"]", "document": { "a'": "A", "b": "B" }, "result": [ "A" ], "result_paths": [ "$['a\\'']" ] }, { "name": "name selector, name, double quotes, nested", "selector": "$[\"a\"][\"b\"][\"c\"]", "document": { "a": { "b": { "c": "C" } } }, "result": [ "C" ], "result_paths": [ "$['a']['b']['c']" ] }, { "name": "name selector, double quotes, embedded U+0000", "selector": "$[\"\u0000\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0001", "selector": "$[\"\u0001\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0002", "selector": "$[\"\u0002\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0003", "selector": "$[\"\u0003\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0004", "selector": "$[\"\u0004\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0005", "selector": "$[\"\u0005\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0006", "selector": "$[\"\u0006\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0007", "selector": "$[\"\u0007\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0008", "selector": "$[\"\b\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0009", "selector": "$[\"\t\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+000A", "selector": "$[\"\n\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+000B", "selector": "$[\"\u000b\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+000C", "selector": "$[\"\f\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+000D", "selector": "$[\"\r\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+000E", "selector": "$[\"\u000e\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+000F", "selector": "$[\"\u000f\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0010", "selector": "$[\"\u0010\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0011", "selector": "$[\"\u0011\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0012", "selector": "$[\"\u0012\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0013", "selector": "$[\"\u0013\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0014", "selector": "$[\"\u0014\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0015", "selector": "$[\"\u0015\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0016", "selector": "$[\"\u0016\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0017", "selector": "$[\"\u0017\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0018", "selector": "$[\"\u0018\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0019", "selector": "$[\"\u0019\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+001A", "selector": "$[\"\u001a\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+001B", "selector": "$[\"\u001b\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+001C", "selector": "$[\"\u001c\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+001D", "selector": "$[\"\u001d\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+001E", "selector": "$[\"\u001e\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+001F", "selector": "$[\"\u001f\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+0020", "selector": "$[\" \"]", "document": { " ": "A" }, "result": [ "A" ], "result_paths": [ "$[' ']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, embedded U+007F", "selector": "$[\"\"]", "document": { "": "A" }, "result": [ "A" ], "result_paths": [ "$['']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, supplementary plane character", "selector": "$[\"𝄞\"]", "document": { "𝄞": "A" }, "result": [ "A" ], "result_paths": [ "$['𝄞']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, escaped double quote", "selector": "$[\"\\\"\"]", "document": { "\"": "A" }, "result": [ "A" ], "result_paths": [ "$['\"']" ] }, { "name": "name selector, double quotes, escaped reverse solidus", "selector": "$[\"\\\\\"]", "document": { "\\": "A" }, "result": [ "A" ], "result_paths": [ "$['\\\\']" ] }, { "name": "name selector, double quotes, escaped solidus", "selector": "$[\"\\/\"]", "document": { "/": "A" }, "result": [ "A" ], "result_paths": [ "$['/']" ] }, { "name": "name selector, double quotes, escaped backspace", "selector": "$[\"\\b\"]", "document": { "\b": "A" }, "result": [ "A" ], "result_paths": [ "$['\\b']" ] }, { "name": "name selector, double quotes, escaped form feed", "selector": "$[\"\\f\"]", "document": { "\f": "A" }, "result": [ "A" ], "result_paths": [ "$['\\f']" ] }, { "name": "name selector, double quotes, escaped line feed", "selector": "$[\"\\n\"]", "document": { "\n": "A" }, "result": [ "A" ], "result_paths": [ "$['\\n']" ] }, { "name": "name selector, double quotes, escaped carriage return", "selector": "$[\"\\r\"]", "document": { "\r": "A" }, "result": [ "A" ], "result_paths": [ "$['\\r']" ] }, { "name": "name selector, double quotes, escaped tab", "selector": "$[\"\\t\"]", "document": { "\t": "A" }, "result": [ "A" ], "result_paths": [ "$['\\t']" ] }, { "name": "name selector, double quotes, escaped ☺, upper case hex", "selector": "$[\"\\u263A\"]", "document": { "☺": "A" }, "result": [ "A" ], "result_paths": [ "$['☺']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, escaped ☺, lower case hex", "selector": "$[\"\\u263a\"]", "document": { "☺": "A" }, "result": [ "A" ], "result_paths": [ "$['☺']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, surrogate pair 𝄞", "selector": "$[\"\\uD834\\uDD1E\"]", "document": { "𝄞": "A" }, "result": [ "A" ], "result_paths": [ "$['𝄞']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, surrogate pair 😀", "selector": "$[\"\\uD83D\\uDE00\"]", "document": { "😀": "A" }, "result": [ "A" ], "result_paths": [ "$['😀']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, before high surrogates", "selector": "$[\"\\uD7FF\\uD7FF\"]", "document": { "퟿퟿": "A" }, "result": [ "A" ], "result_paths": [ "$['퟿퟿']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, after low surrogates", "selector": "$[\"\\uE000\\uE000\"]", "document": { "": "A" }, "result": [ "A" ], "result_paths": [ "$['']" ], "tags": [ "unicode" ] }, { "name": "name selector, double quotes, invalid escaped single quote", "selector": "$[\"\\'\"]", "invalid_selector": true }, { "name": "name selector, double quotes, embedded double quote", "selector": "$[\"\"\"]", "invalid_selector": true }, { "name": "name selector, double quotes, incomplete escape", "selector": "$[\"\\\"]", "invalid_selector": true }, { "name": "name selector, double quotes, escape at end of line", "selector": "$[\"\\\n\"]", "invalid_selector": true }, { "name": "name selector, double quotes, question mark escape", "selector": "$[\"\\?\"]", "invalid_selector": true }, { "name": "name selector, double quotes, bell escape", "selector": "$[\"\\a\"]", "invalid_selector": true }, { "name": "name selector, double quotes, vertical tab escape", "selector": "$[\"\\v\"]", "invalid_selector": true }, { "name": "name selector, double quotes, 0 escape", "selector": "$[\"\\0\"]", "invalid_selector": true }, { "name": "name selector, double quotes, x escape", "selector": "$[\"\\x12\"]", "invalid_selector": true }, { "name": "name selector, double quotes, n escape", "selector": "$[\"\\N{LATIN CAPITAL LETTER A}\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape no hex", "selector": "$[\"\\u\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape too few hex", "selector": "$[\"\\u123\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape upper u", "selector": "$[\"\\U1234\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape upper u long", "selector": "$[\"\\U0010FFFF\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape plus", "selector": "$[\"\\u+1234\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape brackets", "selector": "$[\"\\u{1234}\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, unicode escape brackets long", "selector": "$[\"\\u{10ffff}\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, single high surrogate", "selector": "$[\"\\uD800\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, single low surrogate", "selector": "$[\"\\uDC00\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, high high surrogate", "selector": "$[\"\\uD800\\uD800\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, low low surrogate", "selector": "$[\"\\uDC00\\uDC00\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, surrogate non-surrogate", "selector": "$[\"\\uD800\\u1234\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, non-surrogate surrogate", "selector": "$[\"\\u1234\\uDC00\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, surrogate supplementary", "selector": "$[\"\\uD800𝄞\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, supplementary surrogate", "selector": "$[\"𝄞\\uDC00\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, double quotes, surrogate incomplete low", "selector": "$[\"\\uD800\\uDC0\"]", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes", "selector": "$['a']", "document": { "a": "A", "b": "B" }, "result": [ "A" ], "result_paths": [ "$['a']" ] }, { "name": "name selector, single quotes, absent data", "selector": "$['c']", "document": { "a": "A", "b": "B" }, "result": [], "result_paths": [] }, { "name": "name selector, single quotes, array data", "selector": "$['a']", "document": [ "first", "second" ], "result": [], "result_paths": [] }, { "name": "name selector, single quotes, embedded U+0000", "selector": "$['\u0000']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0001", "selector": "$['\u0001']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0002", "selector": "$['\u0002']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0003", "selector": "$['\u0003']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0004", "selector": "$['\u0004']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0005", "selector": "$['\u0005']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0006", "selector": "$['\u0006']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0007", "selector": "$['\u0007']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0008", "selector": "$['\b']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0009", "selector": "$['\t']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+000A", "selector": "$['\n']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+000B", "selector": "$['\u000b']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+000C", "selector": "$['\f']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+000D", "selector": "$['\r']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+000E", "selector": "$['\u000e']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+000F", "selector": "$['\u000f']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0010", "selector": "$['\u0010']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0011", "selector": "$['\u0011']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0012", "selector": "$['\u0012']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0013", "selector": "$['\u0013']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0014", "selector": "$['\u0014']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0015", "selector": "$['\u0015']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0016", "selector": "$['\u0016']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0017", "selector": "$['\u0017']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0018", "selector": "$['\u0018']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0019", "selector": "$['\u0019']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+001A", "selector": "$['\u001a']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+001B", "selector": "$['\u001b']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+001C", "selector": "$['\u001c']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+001D", "selector": "$['\u001d']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+001E", "selector": "$['\u001e']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+001F", "selector": "$['\u001f']", "invalid_selector": true, "tags": [ "unicode" ] }, { "name": "name selector, single quotes, embedded U+0020", "selector": "$[' ']", "document": { " ": "A" }, "result": [ "A" ], "result_paths": [ "$[' ']" ], "tags": [ "unicode" ] }, { "name": "name selector, single quotes, escaped single quote", "selector": "$['\\'']", "document": { "'": "A" }, "result": [ "A" ], "result_paths": [ "$['\\'']" ] }, { "name": "name selector, single quotes, escaped reverse solidus", "selector": "$['\\\\']", "document": { "\\": "A" }, "result": [ "A" ], "result_paths": [ "$['\\\\']" ] }, { "name": "name selector, single quotes, escaped solidus", "selector": "$['\\/']", "document": { "/": "A" }, "result": [ "A" ], "result_paths": [ "$['/']" ], "tags": [ "unicode" ] }, { "name": "name selector, single quotes, escaped backspace", "selector": "$['\\b']", "document": { "\b": "A" }, "result": [ "A" ], "result_paths": [ "$['\\b']" ] }, { "name": "name selector, single quotes, escaped form feed", "selector": "$['\\f']", "document": { "\f": "A" }, "result": [ "A" ], "result_paths": [ "$['\\f']" ] }, { "name": "name selector, single quotes, escaped line feed", "selector": "$['\\n']", "document": { "\n": "A" }, "result": [ "A" ], "result_paths": [ "$['\\n']" ] }, { "name": "name selector, single quotes, escaped carriage return", "selector": "$['\\r']", "document": { "\r": "A" }, "result": [ "A" ], "result_paths": [ "$['\\r']" ] }, { "name": "name selector, single quotes, escaped tab", "selector": "$['\\t']", "document": { "\t": "A" }, "result": [ "A" ], "result_paths": [ "$['\\t']" ] }, { "name": "name selector, single quotes, escaped ☺, upper case hex", "selector": "$['\\u263A']", "document": { "☺": "A" }, "result": [ "A" ], "result_paths": [ "$['☺']" ], "tags": [ "unicode" ] }, { "name": "name selector, single quotes, escaped ☺, lower case hex", "selector": "$['\\u263a']", "document": { "☺": "A" }, "result": [ "A" ], "result_paths": [ "$['☺']" ], "tags": [ "unicode" ] }, { "name": "name selector, single quotes, surrogate pair 𝄞", "selector": "$['\\uD834\\uDD1E']", "document": { "𝄞": "A" }, "result": [ "A" ], "result_paths": [ "$['𝄞']" ], "tags": [ "unicode" ] }, { "name": "name selector, single quotes, surrogate pair 😀", "selector": "$['\\uD83D\\uDE00']", "document": { "😀": "A" }, "result": [ "A" ], "result_paths": [ "$['😀']" ], "tags": [ "unicode" ] }, { "name": "name selector, single quotes, invalid escaped double quote", "selector": "$['\\\"']", "invalid_selector": true }, { "name": "name selector, single quotes, embedded single quote", "selector": "$[''']", "invalid_selector": true }, { "name": "name selector, single quotes, incomplete escape", "selector": "$['\\']", "invalid_selector": true }, { "name": "name selector, double quotes, empty", "selector": "$[\"\"]", "document": { "a": "A", "b": "B", "": "C" }, "result": [ "C" ], "result_paths": [ "$['']" ] }, { "name": "name selector, single quotes, empty", "selector": "$['']", "document": { "a": "A", "b": "B", "": "C" }, "result": [ "C" ], "result_paths": [ "$['']" ] }, { "name": "slice selector, slice selector", "selector": "$[1:3]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1, 2 ], "result_paths": [ "$[1]", "$[2]" ], "tags": [ "slice" ] }, { "name": "slice selector, slice selector with step", "selector": "$[1:6:2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1, 3, 5 ], "result_paths": [ "$[1]", "$[3]", "$[5]" ], "tags": [ "slice" ] }, { "name": "slice selector, slice selector with everything omitted, short form", "selector": "$[:]", "document": [ 0, 1, 2, 3 ], "result": [ 0, 1, 2, 3 ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]" ], "tags": [ "slice" ] }, { "name": "slice selector, slice selector with everything omitted, long form", "selector": "$[::]", "document": [ 0, 1, 2, 3 ], "result": [ 0, 1, 2, 3 ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]" ], "tags": [ "slice" ] }, { "name": "slice selector, slice selector with start omitted", "selector": "$[:2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0, 1 ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "slice" ] }, { "name": "slice selector, slice selector with start and end omitted", "selector": "$[::2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0, 2, 4, 6, 8 ], "result_paths": [ "$[0]", "$[2]", "$[4]", "$[6]", "$[8]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative step with default start and end", "selector": "$[::-1]", "document": [ 0, 1, 2, 3 ], "result": [ 3, 2, 1, 0 ], "result_paths": [ "$[3]", "$[2]", "$[1]", "$[0]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative step with default start", "selector": "$[:0:-1]", "document": [ 0, 1, 2, 3 ], "result": [ 3, 2, 1 ], "result_paths": [ "$[3]", "$[2]", "$[1]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative step with default end", "selector": "$[2::-1]", "document": [ 0, 1, 2, 3 ], "result": [ 2, 1, 0 ], "result_paths": [ "$[2]", "$[1]", "$[0]" ], "tags": [ "slice" ] }, { "name": "slice selector, larger negative step", "selector": "$[::-2]", "document": [ 0, 1, 2, 3 ], "result": [ 3, 1 ], "result_paths": [ "$[3]", "$[1]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative range with default step", "selector": "$[-1:-3]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [], "result_paths": [], "tags": [ "slice" ] }, { "name": "slice selector, negative range with negative step", "selector": "$[-1:-3:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9, 8 ], "result_paths": [ "$[9]", "$[8]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative range with larger negative step", "selector": "$[-1:-6:-2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9, 7, 5 ], "result_paths": [ "$[9]", "$[7]", "$[5]" ], "tags": [ "slice" ] }, { "name": "slice selector, larger negative range with larger negative step", "selector": "$[-1:-7:-2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9, 7, 5 ], "result_paths": [ "$[9]", "$[7]", "$[5]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative from, positive to", "selector": "$[-5:7]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 5, 6 ], "result_paths": [ "$[5]", "$[6]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative from", "selector": "$[-2:]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 8, 9 ], "result_paths": [ "$[8]", "$[9]" ], "tags": [ "slice" ] }, { "name": "slice selector, positive from, negative to", "selector": "$[1:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1, 2, 3, 4, 5, 6, 7, 8 ], "result_paths": [ "$[1]", "$[2]", "$[3]", "$[4]", "$[5]", "$[6]", "$[7]", "$[8]" ], "tags": [ "slice" ] }, { "name": "slice selector, negative from, positive to, negative step", "selector": "$[-1:1:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9, 8, 7, 6, 5, 4, 3, 2 ], "result_paths": [ "$[9]", "$[8]", "$[7]", "$[6]", "$[5]", "$[4]", "$[3]", "$[2]" ], "tags": [ "slice" ] }, { "name": "slice selector, positive from, negative to, negative step", "selector": "$[7:-5:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 7, 6 ], "result_paths": [ "$[7]", "$[6]" ], "tags": [ "slice" ] }, { "name": "slice selector, in serial, on nested array", "selector": "$[1:3][1:2]", "document": [ [ "a", "b", "c" ], [ "d", "e", "f" ], [ "g", "h", "i" ] ], "result": [ "e", "h" ], "result_paths": [ "$[1][1]", "$[2][1]" ], "tags": [ "slice" ] }, { "name": "slice selector, in serial, on flat array", "selector": "$[1:3][::]", "document": [ 0, 1, 2, 3, 4, 5 ], "result": [], "result_paths": [], "tags": [ "slice" ] }, { "name": "slice selector, negative from, negative to, positive step", "selector": "$[-5:-2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 5, 6, 7 ], "result_paths": [ "$[5]", "$[6]", "$[7]" ], "tags": [ "slice" ] }, { "name": "slice selector, too many colons", "selector": "$[1:2:3:4]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, non-integer array index", "selector": "$[1:2:a]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, zero step", "selector": "$[1:2:0]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [], "result_paths": [], "tags": [ "slice" ] }, { "name": "slice selector, empty range", "selector": "$[2:2]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [], "result_paths": [], "tags": [ "slice" ] }, { "name": "slice selector, slice selector with everything omitted with empty array", "selector": "$[:]", "document": [], "result": [], "result_paths": [], "tags": [ "slice" ] }, { "name": "slice selector, negative step with empty array", "selector": "$[::-1]", "document": [], "result": [], "result_paths": [], "tags": [ "slice" ] }, { "name": "slice selector, maximal range with positive step", "selector": "$[0:10]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result_paths": [ "$[0]", "$[1]", "$[2]", "$[3]", "$[4]", "$[5]", "$[6]", "$[7]", "$[8]", "$[9]" ], "tags": [ "slice" ] }, { "name": "slice selector, maximal range with negative step", "selector": "$[9:0:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9, 8, 7, 6, 5, 4, 3, 2, 1 ], "result_paths": [ "$[9]", "$[8]", "$[7]", "$[6]", "$[5]", "$[4]", "$[3]", "$[2]", "$[1]" ], "tags": [ "slice" ] }, { "name": "slice selector, excessively large to value", "selector": "$[2:113667776004]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 2, 3, 4, 5, 6, 7, 8, 9 ], "result_paths": [ "$[2]", "$[3]", "$[4]", "$[5]", "$[6]", "$[7]", "$[8]", "$[9]" ], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, excessively small from value", "selector": "$[-113667776004:1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 0 ], "result_paths": [ "$[0]" ], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, excessively large from value with negative step", "selector": "$[113667776004:0:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9, 8, 7, 6, 5, 4, 3, 2, 1 ], "result_paths": [ "$[9]", "$[8]", "$[7]", "$[6]", "$[5]", "$[4]", "$[3]", "$[2]", "$[1]" ], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, excessively small to value with negative step", "selector": "$[3:-113667776004:-1]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 3, 2, 1, 0 ], "result_paths": [ "$[3]", "$[2]", "$[1]", "$[0]" ], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, excessively large step", "selector": "$[1:10:113667776004]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 1 ], "result_paths": [ "$[1]" ], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, excessively small step", "selector": "$[-1:-10:-113667776004]", "document": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], "result": [ 9 ], "result_paths": [ "$[9]" ], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, start, min exact", "selector": "$[-9007199254740991::]", "document": [], "result": [], "result_paths": [], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, start, max exact", "selector": "$[9007199254740991::]", "document": [], "result": [], "result_paths": [], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, start, min exact - 1", "selector": "$[-9007199254740992::]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, start, max exact + 1", "selector": "$[9007199254740992::]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, end, min exact", "selector": "$[:-9007199254740991:]", "document": [], "result": [], "result_paths": [], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, end, max exact", "selector": "$[:9007199254740991:]", "document": [], "result": [], "result_paths": [], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, end, min exact - 1", "selector": "$[:-9007199254740992:]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, end, max exact + 1", "selector": "$[:9007199254740992:]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, step, min exact", "selector": "$[::-9007199254740991]", "document": [], "result": [], "result_paths": [], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, step, max exact", "selector": "$[::9007199254740991]", "document": [], "result": [], "result_paths": [], "tags": [ "boundary", "slice" ] }, { "name": "slice selector, step, min exact - 1", "selector": "$[::-9007199254740992]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, step, max exact + 1", "selector": "$[::9007199254740992]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, overflowing to value", "selector": "$[2:231584178474632390847141970017375815706539969331281128078915168015826259279872]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, underflowing from value", "selector": "$[-231584178474632390847141970017375815706539969331281128078915168015826259279872:1]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, overflowing from value with negative step", "selector": "$[231584178474632390847141970017375815706539969331281128078915168015826259279872:0:-1]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, underflowing to value with negative step", "selector": "$[3:-231584178474632390847141970017375815706539969331281128078915168015826259279872:-1]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, overflowing step", "selector": "$[1:10:231584178474632390847141970017375815706539969331281128078915168015826259279872]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, underflowing step", "selector": "$[-1:-10:-231584178474632390847141970017375815706539969331281128078915168015826259279872]", "invalid_selector": true, "tags": [ "boundary", "slice" ] }, { "name": "slice selector, start, leading 0", "selector": "$[01::]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, start, decimal", "selector": "$[1.0::]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, start, plus", "selector": "$[+1::]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, start, minus space", "selector": "$[- 1::]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, start, -0", "selector": "$[-0::]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, start, leading -0", "selector": "$[-01::]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, end, leading 0", "selector": "$[:01:]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, end, decimal", "selector": "$[:1.0:]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, end, plus", "selector": "$[:+1:]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, end, minus space", "selector": "$[:- 1:]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, end, -0", "selector": "$[:-0:]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, end, leading -0", "selector": "$[:-01:]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, step, leading 0", "selector": "$[::01]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, step, decimal", "selector": "$[::1.0]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, step, plus", "selector": "$[::+1]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, step, minus space", "selector": "$[::- 1]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, step, -0", "selector": "$[::-0]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "slice selector, step, leading -0", "selector": "$[::-01]", "invalid_selector": true, "tags": [ "slice" ] }, { "name": "functions, count, count function", "selector": "$[?count(@..*)>2]", "document": [ { "a": [ 1, 2, 3 ] }, { "a": [ 1 ], "d": "f" }, { "a": 1, "d": "f" } ], "result": [ { "a": [ 1, 2, 3 ] }, { "a": [ 1 ], "d": "f" } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function" ] }, { "name": "functions, count, single-node arg", "selector": "$[?count(@.a)>1]", "document": [ { "a": [ 1, 2, 3 ] }, { "a": [ 1 ], "d": "f" }, { "a": 1, "d": "f" } ], "result": [], "result_paths": [], "tags": [ "count", "function" ] }, { "name": "functions, count, multiple-selector arg", "selector": "$[?count(@['a','d'])>1]", "document": [ { "a": [ 1, 2, 3 ] }, { "a": [ 1 ], "d": "f" }, { "a": 1, "d": "f" } ], "result": [ { "a": [ 1 ], "d": "f" }, { "a": 1, "d": "f" } ], "result_paths": [ "$[1]", "$[2]" ], "tags": [ "count", "function" ] }, { "name": "functions, count, non-query arg, number", "selector": "$[?count(1)>2]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, non-query arg, string", "selector": "$[?count('string')>2]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, non-query arg, true", "selector": "$[?count(true)>2]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, non-query arg, false", "selector": "$[?count(false)>2]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, non-query arg, null", "selector": "$[?count(null)>2]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, result must be compared", "selector": "$[?count(@..*)]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, no params", "selector": "$[?count()==1]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, count, too many params", "selector": "$[?count(@.a,@.b)==1]", "invalid_selector": true, "tags": [ "count", "function" ] }, { "name": "functions, length, string data", "selector": "$[?length(@.a)>=2]", "document": [ { "a": "ab" }, { "a": "d" } ], "result": [ { "a": "ab" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length" ] }, { "name": "functions, length, string data, unicode", "selector": "$[?length(@)==2]", "document": [ "☺", "☺☺", "☺☺☺", "ж", "жж", "жжж", "磨", "阿美", "形声字" ], "result": [ "☺☺", "жж", "阿美" ], "result_paths": [ "$[1]", "$[4]", "$[7]" ], "tags": [ "function", "length" ] }, { "name": "functions, length, array data", "selector": "$[?length(@.a)>=2]", "document": [ { "a": [ 1, 2, 3 ] }, { "a": [ 1 ] } ], "result": [ { "a": [ 1, 2, 3 ] } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length" ] }, { "name": "functions, length, missing data", "selector": "$[?length(@.a)>=2]", "document": [ { "d": "f" } ], "result": [], "result_paths": [], "tags": [ "function", "length" ] }, { "name": "functions, length, number arg", "selector": "$[?length(1)>=2]", "document": [ { "d": "f" } ], "result": [], "result_paths": [], "tags": [ "function", "length" ] }, { "name": "functions, length, true arg", "selector": "$[?length(true)>=2]", "document": [ { "d": "f" } ], "result": [], "result_paths": [], "tags": [ "function", "length" ] }, { "name": "functions, length, false arg", "selector": "$[?length(false)>=2]", "document": [ { "d": "f" } ], "result": [], "result_paths": [], "tags": [ "function", "length" ] }, { "name": "functions, length, null arg", "selector": "$[?length(null)>=2]", "document": [ { "d": "f" } ], "result": [], "result_paths": [], "tags": [ "function", "length" ] }, { "name": "functions, length, result must be compared", "selector": "$[?length(@.a)]", "invalid_selector": true, "tags": [ "function", "length" ] }, { "name": "functions, length, no params", "selector": "$[?length()==1]", "invalid_selector": true, "tags": [ "function", "length" ] }, { "name": "functions, length, too many params", "selector": "$[?length(@.a,@.b)==1]", "invalid_selector": true, "tags": [ "function", "length" ] }, { "name": "functions, length, non-singular query arg", "selector": "$[?length(@.*)<3]", "invalid_selector": true, "tags": [ "function", "length" ] }, { "name": "functions, length, arg is a function expression", "selector": "$.values[?length(@.a)==length(value($..c))]", "document": { "c": "cd", "values": [ { "a": "ab" }, { "a": "d" } ] }, "result": [ { "a": "ab" } ], "result_paths": [ "$['values'][0]" ], "tags": [ "function", "length" ] }, { "name": "functions, length, arg is special nothing", "selector": "$[?length(value(@.a))>0]", "document": [ { "a": "ab" }, { "c": "d" }, { "a": null } ], "result": [ { "a": "ab" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length" ] }, { "name": "functions, match, found match", "selector": "$[?match(@.a, 'a.*')]", "document": [ { "a": "ab" } ], "result": [ { "a": "ab" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, double quotes", "selector": "$[?match(@.a, \"a.*\")]", "document": [ { "a": "ab" } ], "result": [ { "a": "ab" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, regex from the document", "selector": "$.values[?match(@, $.regex)]", "document": { "regex": "b.?b", "values": [ "abc", "bcd", "bab", "bba", "bbab", "b", true, [], {} ] }, "result": [ "bab" ], "result_paths": [ "$['values'][2]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, don't select match", "selector": "$[?!match(@.a, 'a.*')]", "document": [ { "a": "ab" } ], "result": [], "result_paths": [], "tags": [ "function", "match" ] }, { "name": "functions, match, not a match", "selector": "$[?match(@.a, 'a.*')]", "document": [ { "a": "bc" } ], "result": [], "result_paths": [], "tags": [ "function", "match" ] }, { "name": "functions, match, select non-match", "selector": "$[?!match(@.a, 'a.*')]", "document": [ { "a": "bc" } ], "result": [ { "a": "bc" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, non-string first arg", "selector": "$[?match(1, 'a.*')]", "document": [ { "a": "bc" } ], "result": [], "result_paths": [], "tags": [ "function", "match" ] }, { "name": "functions, match, non-string second arg", "selector": "$[?match(@.a, 1)]", "document": [ { "a": "bc" } ], "result": [], "result_paths": [], "tags": [ "function", "match" ] }, { "name": "functions, match, filter, match function, unicode char class, uppercase", "selector": "$[?match(@, '\\\\p{Lu}')]", "document": [ "ж", "Ж", "1", "жЖ", true, [], {} ], "result": [ "Ж" ], "result_paths": [ "$[1]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, filter, match function, unicode char class negated, uppercase", "selector": "$[?match(@, '\\\\P{Lu}')]", "document": [ "ж", "Ж", "1", true, [], {} ], "result": [ "ж", "1" ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, filter, match function, unicode, surrogate pair", "selector": "$[?match(@, 'a.b')]", "document": [ "a𐄁b", "ab", "1", true, [], {} ], "result": [ "a𐄁b" ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, dot matcher on \\u2028", "selector": "$[?match(@, '.')]", "document": [ "
", "\r", "\n", true, [], {} ], "result": [ "
" ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, dot matcher on \\u2029", "selector": "$[?match(@, '.')]", "document": [ "
", "\r", "\n", true, [], {} ], "result": [ "
" ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, result cannot be compared", "selector": "$[?match(@.a, 'a.*')==true]", "invalid_selector": true, "tags": [ "function", "match" ] }, { "name": "functions, match, too few params", "selector": "$[?match(@.a)==1]", "invalid_selector": true, "tags": [ "function", "match" ] }, { "name": "functions, match, too many params", "selector": "$[?match(@.a,@.b,@.c)==1]", "invalid_selector": true, "tags": [ "function", "match" ] }, { "name": "functions, match, arg is a function expression", "selector": "$.values[?match(@.a, value($..['regex']))]", "document": { "regex": "a.*", "values": [ { "a": "ab" }, { "a": "ba" } ] }, "result": [ { "a": "ab" } ], "result_paths": [ "$['values'][0]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, dot in character class", "selector": "$[?match(@, 'a[.b]c')]", "document": [ "abc", "a.c", "axc" ], "result": [ "abc", "a.c" ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, escaped dot", "selector": "$[?match(@, 'a\\\\.c')]", "document": [ "abc", "a.c", "axc" ], "result": [ "a.c" ], "result_paths": [ "$[1]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, escaped backslash before dot", "selector": "$[?match(@, 'a\\\\\\\\.c')]", "document": [ "abc", "a.c", "axc", "a\\
c" ], "result": [ "a\\
c" ], "result_paths": [ "$[3]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, escaped left square bracket", "selector": "$[?match(@, 'a\\\\[.c')]", "document": [ "abc", "a.c", "a[
c" ], "result": [ "a[
c" ], "result_paths": [ "$[2]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, escaped right square bracket", "selector": "$[?match(@, 'a[\\\\].]c')]", "document": [ "abc", "a.c", "a
c", "a]c" ], "result": [ "a.c", "a]c" ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, explicit caret", "selector": "$[?match(@, '^ab.*')]", "document": [ "abc", "axc", "ab", "xab" ], "result": [ "abc", "ab" ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "function", "match" ] }, { "name": "functions, match, explicit dollar", "selector": "$[?match(@, '.*bc$')]", "document": [ "abc", "axc", "ab", "abcx" ], "result": [ "abc" ], "result_paths": [ "$[0]" ], "tags": [ "function", "match" ] }, { "name": "functions, search, at the end", "selector": "$[?search(@.a, 'a.*')]", "document": [ { "a": "the end is ab" } ], "result": [ { "a": "the end is ab" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, double quotes", "selector": "$[?search(@.a, \"a.*\")]", "document": [ { "a": "the end is ab" } ], "result": [ { "a": "the end is ab" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, at the start", "selector": "$[?search(@.a, 'a.*')]", "document": [ { "a": "ab is at the start" } ], "result": [ { "a": "ab is at the start" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, in the middle", "selector": "$[?search(@.a, 'a.*')]", "document": [ { "a": "contains two matches" } ], "result": [ { "a": "contains two matches" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, regex from the document", "selector": "$.values[?search(@, $.regex)]", "document": { "regex": "b.?b", "values": [ "abc", "bcd", "bab", "bba", "bbab", "b", true, [], {} ] }, "result": [ "bab", "bba", "bbab" ], "result_paths": [ "$['values'][2]", "$['values'][3]", "$['values'][4]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, don't select match", "selector": "$[?!search(@.a, 'a.*')]", "document": [ { "a": "contains two matches" } ], "result": [], "result_paths": [], "tags": [ "function", "search" ] }, { "name": "functions, search, not a match", "selector": "$[?search(@.a, 'a.*')]", "document": [ { "a": "bc" } ], "result": [], "result_paths": [], "tags": [ "function", "search" ] }, { "name": "functions, search, select non-match", "selector": "$[?!search(@.a, 'a.*')]", "document": [ { "a": "bc" } ], "result": [ { "a": "bc" } ], "result_paths": [ "$[0]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, non-string first arg", "selector": "$[?search(1, 'a.*')]", "document": [ { "a": "bc" } ], "result": [], "result_paths": [], "tags": [ "function", "search" ] }, { "name": "functions, search, non-string second arg", "selector": "$[?search(@.a, 1)]", "document": [ { "a": "bc" } ], "result": [], "result_paths": [], "tags": [ "function", "search" ] }, { "name": "functions, search, filter, search function, unicode char class, uppercase", "selector": "$[?search(@, '\\\\p{Lu}')]", "document": [ "ж", "Ж", "1", "жЖ", true, [], {} ], "result": [ "Ж", "жЖ" ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, filter, search function, unicode char class negated, uppercase", "selector": "$[?search(@, '\\\\P{Lu}')]", "document": [ "ж", "Ж", "1", true, [], {} ], "result": [ "ж", "1" ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, filter, search function, unicode, surrogate pair", "selector": "$[?search(@, 'a.b')]", "document": [ "a𐄁bc", "abc", "1", true, [], {} ], "result": [ "a𐄁bc" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, dot matcher on \\u2028", "selector": "$[?search(@, '.')]", "document": [ "
", "\r
\n", "\r", "\n", true, [], {} ], "result": [ "
", "\r
\n" ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, dot matcher on \\u2029", "selector": "$[?search(@, '.')]", "document": [ "
", "\r
\n", "\r", "\n", true, [], {} ], "result": [ "
", "\r
\n" ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, result cannot be compared", "selector": "$[?search(@.a, 'a.*')==true]", "invalid_selector": true, "tags": [ "function", "search" ] }, { "name": "functions, search, too few params", "selector": "$[?search(@.a)]", "invalid_selector": true, "tags": [ "function", "search" ] }, { "name": "functions, search, too many params", "selector": "$[?search(@.a,@.b,@.c)]", "invalid_selector": true, "tags": [ "function", "search" ] }, { "name": "functions, search, arg is a function expression", "selector": "$.values[?search(@, value($..['regex']))]", "document": { "regex": "b.?b", "values": [ "abc", "bcd", "bab", "bba", "bbab", "b", true, [], {} ] }, "result": [ "bab", "bba", "bbab" ], "result_paths": [ "$['values'][2]", "$['values'][3]", "$['values'][4]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, dot in character class", "selector": "$[?search(@, 'a[.b]c')]", "document": [ "x abc y", "x a.c y", "x axc y" ], "result": [ "x abc y", "x a.c y" ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, escaped dot", "selector": "$[?search(@, 'a\\\\.c')]", "document": [ "x abc y", "x a.c y", "x axc y" ], "result": [ "x a.c y" ], "result_paths": [ "$[1]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, escaped backslash before dot", "selector": "$[?search(@, 'a\\\\\\\\.c')]", "document": [ "x abc y", "x a.c y", "x axc y", "x a\\
c y" ], "result": [ "x a\\
c y" ], "result_paths": [ "$[3]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, escaped left square bracket", "selector": "$[?search(@, 'a\\\\[.c')]", "document": [ "x abc y", "x a.c y", "x a[
c y" ], "result": [ "x a[
c y" ], "result_paths": [ "$[2]" ], "tags": [ "function", "search" ] }, { "name": "functions, search, escaped right square bracket", "selector": "$[?search(@, 'a[\\\\].]c')]", "document": [ "x abc y", "x a.c y", "x a
c y", "x a]c y" ], "result": [ "x a.c y", "x a]c y" ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "function", "search" ] }, { "name": "functions, value, single-value nodelist", "selector": "$[?value(@.*)==4]", "document": [ [ 4 ], { "foo": 4 }, [ 5 ], { "foo": 5 }, 4 ], "result": [ [ 4 ], { "foo": 4 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "function", "value" ] }, { "name": "functions, value, multi-value nodelist", "selector": "$[?value(@.*)==4]", "document": [ [ 4, 4 ], { "foo": 4, "bar": 4 } ], "result": [], "result_paths": [], "tags": [ "function", "value" ] }, { "name": "functions, value, too few params", "selector": "$[?value()==4]", "invalid_selector": true, "tags": [ "function", "value" ] }, { "name": "functions, value, too many params", "selector": "$[?value(@.a,@.b)==4]", "invalid_selector": true, "tags": [ "function", "value" ] }, { "name": "functions, value, result must be compared", "selector": "$[?value(@.a)]", "invalid_selector": true, "tags": [ "function", "value" ] }, { "name": "whitespace, filter, space between question mark and expression", "selector": "$[? @.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, newline between question mark and expression", "selector": "$[?\n@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, tab between question mark and expression", "selector": "$[?\t@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, return between question mark and expression", "selector": "$[?\r@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, space between question mark and parenthesized expression", "selector": "$[? (@.a)]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, newline between question mark and parenthesized expression", "selector": "$[?\n(@.a)]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, tab between question mark and parenthesized expression", "selector": "$[?\t(@.a)]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, return between question mark and parenthesized expression", "selector": "$[?\r(@.a)]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, space between parenthesized expression and bracket", "selector": "$[?(@.a) ]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, newline between parenthesized expression and bracket", "selector": "$[?(@.a)\n]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, tab between parenthesized expression and bracket", "selector": "$[?(@.a)\t]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, return between parenthesized expression and bracket", "selector": "$[?(@.a)\r]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, space between bracket and question mark", "selector": "$[ ?@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, newline between bracket and question mark", "selector": "$[\n?@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, tab between bracket and question mark", "selector": "$[\t?@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, filter, return between bracket and question mark", "selector": "$[\r?@.a]", "document": [ { "a": "b", "d": "e" }, { "b": "c", "d": "f" } ], "result": [ { "a": "b", "d": "e" } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, functions, space between function name and parenthesis", "selector": "$[?count (@.*)==1]", "invalid_selector": true, "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, newline between function name and parenthesis", "selector": "$[?count\n(@.*)==1]", "invalid_selector": true, "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, tab between function name and parenthesis", "selector": "$[?count\t(@.*)==1]", "invalid_selector": true, "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, return between function name and parenthesis", "selector": "$[?count\r(@.*)==1]", "invalid_selector": true, "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, space between parenthesis and arg", "selector": "$[?count( @.*)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, newline between parenthesis and arg", "selector": "$[?count(\n@.*)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, tab between parenthesis and arg", "selector": "$[?count(\t@.*)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, return between parenthesis and arg", "selector": "$[?count(\r@.*)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, space between arg and comma", "selector": "$[?search(@ ,'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, newline between arg and comma", "selector": "$[?search(@\n,'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, tab between arg and comma", "selector": "$[?search(@\t,'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, return between arg and comma", "selector": "$[?search(@\r,'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, space between comma and arg", "selector": "$[?search(@, '[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, newline between comma and arg", "selector": "$[?search(@,\n'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, tab between comma and arg", "selector": "$[?search(@,\t'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, return between comma and arg", "selector": "$[?search(@,\r'[a-z]+')]", "document": [ "foo", "123" ], "result": [ "foo" ], "result_paths": [ "$[0]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, space between arg and parenthesis", "selector": "$[?count(@.* )==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "function", "search", "whitespace" ] }, { "name": "whitespace, functions, newline between arg and parenthesis", "selector": "$[?count(@.*\n)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, tab between arg and parenthesis", "selector": "$[?count(@.*\t)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, return between arg and parenthesis", "selector": "$[?count(@.*\r)==1]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "count", "function", "whitespace" ] }, { "name": "whitespace, functions, spaces in a relative singular selector", "selector": "$[?length(@ .a .b) == 3]", "document": [ { "a": { "b": "foo" } }, {} ], "result": [ { "a": { "b": "foo" } } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, newlines in a relative singular selector", "selector": "$[?length(@\n.a\n.b) == 3]", "document": [ { "a": { "b": "foo" } }, {} ], "result": [ { "a": { "b": "foo" } } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, tabs in a relative singular selector", "selector": "$[?length(@\t.a\t.b) == 3]", "document": [ { "a": { "b": "foo" } }, {} ], "result": [ { "a": { "b": "foo" } } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, returns in a relative singular selector", "selector": "$[?length(@\r.a\r.b) == 3]", "document": [ { "a": { "b": "foo" } }, {} ], "result": [ { "a": { "b": "foo" } } ], "result_paths": [ "$[0]" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, spaces in an absolute singular selector", "selector": "$..[?length(@)==length($ [0] .a)]", "document": [ { "a": "foo" }, {} ], "result": [ "foo" ], "result_paths": [ "$[0]['a']" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, newlines in an absolute singular selector", "selector": "$..[?length(@)==length($\n[0]\n.a)]", "document": [ { "a": "foo" }, {} ], "result": [ "foo" ], "result_paths": [ "$[0]['a']" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, tabs in an absolute singular selector", "selector": "$..[?length(@)==length($\t[0]\t.a)]", "document": [ { "a": "foo" }, {} ], "result": [ "foo" ], "result_paths": [ "$[0]['a']" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, functions, returns in an absolute singular selector", "selector": "$..[?length(@)==length($\r[0]\r.a)]", "document": [ { "a": "foo" }, {} ], "result": [ "foo" ], "result_paths": [ "$[0]['a']" ], "tags": [ "function", "length", "whitespace" ] }, { "name": "whitespace, operators, space before ||", "selector": "$[?@.a ||@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before ||", "selector": "$[?@.a\n||@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before ||", "selector": "$[?@.a\t||@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before ||", "selector": "$[?@.a\r||@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after ||", "selector": "$[?@.a|| @.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after ||", "selector": "$[?@.a||\n@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after ||", "selector": "$[?@.a||\t@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after ||", "selector": "$[?@.a||\r@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "c": 3 } ], "result": [ { "a": 1 }, { "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before &&", "selector": "$[?@.a &&@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before &&", "selector": "$[?@.a\n&&@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before &&", "selector": "$[?@.a\t&&@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before &&", "selector": "$[?@.a\r&&@.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after &&", "selector": "$[?@.a&& @.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after &&", "selector": "$[?@.a&& @.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after &&", "selector": "$[?@.a&& @.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after &&", "selector": "$[?@.a&& @.b]", "document": [ { "a": 1 }, { "b": 2 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before ==", "selector": "$[?@.a ==@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before ==", "selector": "$[?@.a\n==@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before ==", "selector": "$[?@.a\t==@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before ==", "selector": "$[?@.a\r==@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after ==", "selector": "$[?@.a== @.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after ==", "selector": "$[?@.a==\n@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after ==", "selector": "$[?@.a==\t@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after ==", "selector": "$[?@.a==\r@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 1 } ], "result_paths": [ "$[0]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before !=", "selector": "$[?@.a !=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before !=", "selector": "$[?@.a\n!=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before !=", "selector": "$[?@.a\t!=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before !=", "selector": "$[?@.a\r!=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after !=", "selector": "$[?@.a!= @.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after !=", "selector": "$[?@.a!=\n@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after !=", "selector": "$[?@.a!=\t@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after !=", "selector": "$[?@.a!=\r@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before <", "selector": "$[?@.a <@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before <", "selector": "$[?@.a\n<@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before <", "selector": "$[?@.a\t<@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before <", "selector": "$[?@.a\r<@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after <", "selector": "$[?@.a< @.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after <", "selector": "$[?@.a<\n@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after <", "selector": "$[?@.a<\t@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after <", "selector": "$[?@.a<\r@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before >", "selector": "$[?@.b >@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before >", "selector": "$[?@.b\n>@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before >", "selector": "$[?@.b\t>@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before >", "selector": "$[?@.b\r>@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after >", "selector": "$[?@.b> @.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after >", "selector": "$[?@.b>\n@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after >", "selector": "$[?@.b>\t@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after >", "selector": "$[?@.b>\r@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result": [ { "a": 1, "b": 2 } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before <=", "selector": "$[?@.a <=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before <=", "selector": "$[?@.a\n<=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before <=", "selector": "$[?@.a\t<=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before <=", "selector": "$[?@.a\r<=@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after <=", "selector": "$[?@.a<= @.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after <=", "selector": "$[?@.a<=\n@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after <=", "selector": "$[?@.a<=\t@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after <=", "selector": "$[?@.a<=\r@.b]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space before >=", "selector": "$[?@.b >=@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline before >=", "selector": "$[?@.b\n>=@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab before >=", "selector": "$[?@.b\t>=@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return before >=", "selector": "$[?@.b\r>=@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space after >=", "selector": "$[?@.b>= @.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline after >=", "selector": "$[?@.b>=\n@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab after >=", "selector": "$[?@.b>=\t@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return after >=", "selector": "$[?@.b>=\r@.a]", "document": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 }, { "a": 2, "b": 1 } ], "result": [ { "a": 1, "b": 1 }, { "a": 1, "b": 2 } ], "result_paths": [ "$[0]", "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space between logical not and test expression", "selector": "$[?! @.a]", "document": [ { "a": "a", "d": "e" }, { "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "d": "f" } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline between logical not and test expression", "selector": "$[?!\n@.a]", "document": [ { "a": "a", "d": "e" }, { "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "d": "f" } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab between logical not and test expression", "selector": "$[?!\t@.a]", "document": [ { "a": "a", "d": "e" }, { "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "d": "f" } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return between logical not and test expression", "selector": "$[?!\r@.a]", "document": [ { "a": "a", "d": "e" }, { "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "d": "f" } ], "result_paths": [ "$[1]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, space between logical not and parenthesized expression", "selector": "$[?! (@.a=='b')]", "document": [ { "a": "a", "d": "e" }, { "a": "b", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "a", "d": "e" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, newline between logical not and parenthesized expression", "selector": "$[?!\n(@.a=='b')]", "document": [ { "a": "a", "d": "e" }, { "a": "b", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "a", "d": "e" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, tab between logical not and parenthesized expression", "selector": "$[?!\t(@.a=='b')]", "document": [ { "a": "a", "d": "e" }, { "a": "b", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "a", "d": "e" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, operators, return between logical not and parenthesized expression", "selector": "$[?!\r(@.a=='b')]", "document": [ { "a": "a", "d": "e" }, { "a": "b", "d": "f" }, { "a": "d", "d": "f" } ], "result": [ { "a": "a", "d": "e" }, { "a": "d", "d": "f" } ], "result_paths": [ "$[0]", "$[2]" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between root and bracket", "selector": "$ ['a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between root and bracket", "selector": "$\n['a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between root and bracket", "selector": "$\t['a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between root and bracket", "selector": "$\r['a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between bracket and bracket", "selector": "$['a'] ['b']", "document": { "a": { "b": "ab" } }, "result": [ "ab" ], "result_paths": [ "$['a']['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between bracket and bracket", "selector": "$['a'] \n['b']", "document": { "a": { "b": "ab" } }, "result": [ "ab" ], "result_paths": [ "$['a']['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between bracket and bracket", "selector": "$['a'] \t['b']", "document": { "a": { "b": "ab" } }, "result": [ "ab" ], "result_paths": [ "$['a']['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between bracket and bracket", "selector": "$['a'] \r['b']", "document": { "a": { "b": "ab" } }, "result": [ "ab" ], "result_paths": [ "$['a']['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between root and dot", "selector": "$ .a", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between root and dot", "selector": "$\n.a", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between root and dot", "selector": "$\t.a", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between root and dot", "selector": "$\r.a", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between dot and name", "selector": "$. a", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between dot and name", "selector": "$.\na", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between dot and name", "selector": "$.\ta", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between dot and name", "selector": "$.\ra", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between recursive descent and name", "selector": "$.. a", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between recursive descent and name", "selector": "$..\na", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between recursive descent and name", "selector": "$..\ta", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between recursive descent and name", "selector": "$..\ra", "invalid_selector": true, "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between bracket and selector", "selector": "$[ 'a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between bracket and selector", "selector": "$[\n'a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between bracket and selector", "selector": "$[\t'a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between bracket and selector", "selector": "$[\r'a']", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between selector and bracket", "selector": "$['a' ]", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between selector and bracket", "selector": "$['a'\n]", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between selector and bracket", "selector": "$['a'\t]", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between selector and bracket", "selector": "$['a'\r]", "document": { "a": "ab" }, "result": [ "ab" ], "result_paths": [ "$['a']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between selector and comma", "selector": "$['a' ,'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between selector and comma", "selector": "$['a'\n,'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between selector and comma", "selector": "$['a'\t,'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between selector and comma", "selector": "$['a'\r,'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, space between comma and selector", "selector": "$['a', 'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, newline between comma and selector", "selector": "$['a',\n'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, tab between comma and selector", "selector": "$['a',\t'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, selectors, return between comma and selector", "selector": "$['a',\r'b']", "document": { "a": "ab", "b": "bc" }, "result": [ "ab", "bc" ], "result_paths": [ "$['a']", "$['b']" ], "tags": [ "whitespace" ] }, { "name": "whitespace, slice, space between start and colon", "selector": "$[1 :5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, newline between start and colon", "selector": "$[1\n:5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, tab between start and colon", "selector": "$[1\t:5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, return between start and colon", "selector": "$[1\r:5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, space between colon and end", "selector": "$[1: 5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, newline between colon and end", "selector": "$[1:\n5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, tab between colon and end", "selector": "$[1:\t5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, return between colon and end", "selector": "$[1:\r5:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, space between end and colon", "selector": "$[1:5 :2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, newline between end and colon", "selector": "$[1:5\n:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, tab between end and colon", "selector": "$[1:5\t:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, return between end and colon", "selector": "$[1:5\r:2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, space between colon and step", "selector": "$[1:5: 2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, newline between colon and step", "selector": "$[1:5:\n2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, tab between colon and step", "selector": "$[1:5:\t2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] }, { "name": "whitespace, slice, return between colon and step", "selector": "$[1:5:\r2]", "document": [ 1, 2, 3, 4, 5, 6 ], "result": [ 2, 4 ], "result_paths": [ "$[1]", "$[3]" ], "tags": [ "index", "whitespace" ] } ] } aeson-jsonpath-0.3.0.2/src/Data/Aeson/0000755000000000000000000000000007346545000015501 5ustar0000000000000000aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath.hs0000644000000000000000000000662507346545000017434 0ustar0000000000000000{- | Module : Data.Aeson.JSONPath Description : Run JSONPath queries on Data.Aeson Copyright : (c) 2024-2025 Taimoor Zaeem License : MIT Maintainer : Taimoor Zaeem Stability : Experimental Portability : Portable Run JSONPath queries on Aeson Values using methods exported in this module. -} module Data.Aeson.JSONPath ( -- * Using this library -- $use -- * API query , queryQQ , queryLocated , queryLocatedQQ -- * QuasiQuoter , jsonPath ) where import qualified Text.ParserCombinators.Parsec as P import Data.Aeson (Value) import Data.Vector (Vector) import Language.Haskell.TH.Quote (QuasiQuoter (..)) import Language.Haskell.TH.Syntax (lift) import Data.Aeson.JSONPath.Parser (pQuery) import Data.Aeson.JSONPath.Query (qQuery, qQueryLocated) import Data.Aeson.JSONPath.Types import Prelude -- | -- A 'QuasiQuoter' for checking valid JSONPath syntax at compile time -- -- @ -- path :: Query -- path = [jsonPath|$.store.records[0,1]|] -- @ jsonPath :: QuasiQuoter jsonPath = QuasiQuoter { quoteExp = \q -> case P.parse pQuery ("failed to parse query: " <> q) q of Left err -> fail $ show err Right ex -> lift ex , quotePat = error "Error: quotePat" , quoteType = error "Error: quoteType" , quoteDec = error "Error: quoteDec" } -- | -- Use when query string is not known at compile time -- -- @ -- >>> query "$.artist" json -- Right [String "David Bowie"] -- -- >>> query "$.art[ist" json -- Left "failed to parse query: $.art[ist" (line 1, column 7) -- @ -- For detailed usage examples, see: query :: String -> Value -> Either P.ParseError (Vector Value) query q root = do parsedQuery <- P.parse pQuery ("failed to parse query: " <> q) q return $ queryQQ parsedQuery root -- | -- Use when query string is known at compile time -- -- @ -- artist = queryQQ [jsonPath|$.artist|] json -- successfully compiles -- -- >>> artist -- [String "David Bowie"] -- @ -- @ -- artist = queryQQ [jsonPath|$.art[ist|] json -- fails at compilation time -- @ queryQQ :: Query -> Value -> Vector Value queryQQ q root = qQuery q QueryState { rootVal = root , curVal = root , executeQuery = qQuery } -- | -- Get the location of the returned nodes along with the node -- -- @ -- >>> queryLocated "$.title" json -- Right [("$[\'title\']",String "Space Oddity")] -- @ queryLocated :: String -> Value -> Either P.ParseError (Vector (String, Value)) queryLocated q root = do parsedQuery <- P.parse pQuery ("failed to parse query: " <> q) q return $ queryLocatedQQ parsedQuery root -- | -- Same as 'queryLocated' but allows QuasiQuoter -- -- @ -- artist = queryLocatedQQ [jsonPath|$.*|] json -- successfully compiles -- -- >>> artist -- [("$[\'artist\']",String "David Bowie"), -- ("$[\'title\']",String "Space Oddity")] -- @ queryLocatedQQ :: Query -> Value -> Vector (String, Value) queryLocatedQQ q root = qQueryLocated q QueryState { rootVal = root , curVal = root , executeQuery = qQuery } "$" -- $use -- -- To use this package, I would suggest that you import this module like: -- -- > {-# LANGUAGE QuasiQuotes #-} -- > import qualified Data.Aeson.JSONPath as JSONPath -- > import Data.Aeson.JSONPath (jsonPath) -- -- For this module, consider this json for the example queries -- -- > { "artist": "David Bowie", "title": "Space Oddity" } aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/0000755000000000000000000000000007346545000017067 5ustar0000000000000000aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser.hs0000644000000000000000000000117107346545000020657 0ustar0000000000000000{-# OPTIONS_GHC -Wno-unused-do-bind #-} {- | Module : Data.Aeson.JSONPath.Parser Description : JSONPath Query Parser Copyright : (c) 2024-2025 Taimoor Zaeem License : MIT Maintainer : Taimoor Zaeem Stability : Experimental Portability : Portable This module is responsible for parsing the JSONPath query -} module Data.Aeson.JSONPath.Parser ( pQuery ) where import qualified Text.ParserCombinators.Parsec as P import Data.Aeson.JSONPath.Parser.Query (pRootQuery) import Data.Aeson.JSONPath.Types import Prelude -- | Query parser pQuery :: P.Parser Query pQuery = pRootQuery <* P.eof aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser/0000755000000000000000000000000007346545000020323 5ustar0000000000000000aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser/Common.hs0000644000000000000000000000076007346545000022112 0ustar0000000000000000module Data.Aeson.JSONPath.Parser.Common ( pSpaces , pUnicodeChar ) where import qualified Text.ParserCombinators.Parsec as P import Data.Char (ord) import Prelude -- https://www.rfc-editor.org/rfc/rfc9535#name-syntax pSpaces :: P.Parser [Char] pSpaces = P.many (P.oneOf " \n\r\t") pUnicodeChar :: P.Parser Char pUnicodeChar = P.satisfy inRange where inRange c = let code = ord c in (code >= 0x80 && code <= 0xD7FF) || (code >= 0xE000 && code <= 0x10FFFF) aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser/Filter.hs0000644000000000000000000001121607346545000022105 0ustar0000000000000000{-# OPTIONS_GHC -Wno-unused-do-bind #-} module Data.Aeson.JSONPath.Parser.Filter ( pFilter ) where import qualified Data.Text as T import qualified Text.ParserCombinators.Parsec as P import Data.Functor (($>)) import Data.Maybe (isNothing) import Data.Scientific (Scientific) import Text.ParserCombinators.Parsec ((<|>)) import Data.Aeson.JSONPath.Parser.Name import Data.Aeson.JSONPath.Parser.Number import Data.Aeson.JSONPath.Parser.Common import Data.Aeson.JSONPath.Types import Prelude pFilter :: P.Parser a -> P.Parser (Selector a) pFilter pQ = do P.char '?' pSpaces Filter <$> pLogicalOrExpr pQ pLogicalOrExpr :: P.Parser a -> P.Parser (LogicalOrExpr a) pLogicalOrExpr pQ = do expr <- pLogicalAndExpr pQ optionalExprs <- P.many $ pOrSepLogicalAndExprs pQ return $ LogicalOr (expr:optionalExprs) where pOrSepLogicalAndExprs :: P.Parser a -> P.Parser (LogicalAndExpr a) pOrSepLogicalAndExprs pQ' = P.try $ pSpaces *> P.string "||" *> pSpaces *> pLogicalAndExpr pQ' pLogicalAndExpr :: P.Parser a -> P.Parser (LogicalAndExpr a) pLogicalAndExpr pQ = do expr <- pBasicExpr pQ optionalExprs <- P.many $ pAndSepBasicExprs pQ return $ LogicalAnd (expr:optionalExprs) where pAndSepBasicExprs :: P.Parser a -> P.Parser (BasicExpr a) pAndSepBasicExprs pQ' = P.try $ pSpaces *> P.string "&&" *> pSpaces *> pBasicExpr pQ' pBasicExpr :: P.Parser a -> P.Parser (BasicExpr a) pBasicExpr pQ = P.try (pParenExpr pQ) <|> P.try pComparisonExpr <|> P.try (pTestExpr pQ) pParenExpr :: P.Parser a -> P.Parser (BasicExpr a) pParenExpr pQ = do notOp <- P.optionMaybe (P.char '!' <* pSpaces) P.char '(' pSpaces expr <- pLogicalOrExpr pQ pSpaces P.char ')' let parenExp = if isNothing notOp then Paren expr else NotParen expr return parenExp pTestExpr :: P.Parser a -> P.Parser (BasicExpr a) pTestExpr pQ = do notOp <- P.optionMaybe (P.char '!' <* pSpaces) q <- P.try (FilterQuery <$> pQ) let testExp = if isNothing notOp then Test q else NotTest q return testExp pComparisonExpr :: P.Parser (BasicExpr a) pComparisonExpr = do leftC <- pComparable pSpaces compOp <- pComparisonOp pSpaces Comparison . Comp leftC compOp <$> pComparable pComparisonOp :: P.Parser ComparisonOp pComparisonOp = P.try (P.string ">=" $> GreaterOrEqual) <|> P.try (P.string "<=" $> LessOrEqual) <|> P.try (P.char '>' $> Greater) <|> P.try (P.char '<' $> Less) <|> P.try (P.string "!=" $> NotEqual) <|> P.try (P.string "==" $> Equal) pComparable :: P.Parser Comparable pComparable = P.try pCompLit <|> P.try pCompSQ pCompLit :: P.Parser Comparable pCompLit = CompLit <$> (P.try pLitString <|> P.try pLitNum <|> P.try pLitBool <|> P.try pLitNull) pLitString :: P.Parser Literal pLitString = LitString . T.pack <$> (P.try pSingleQuotted <|> P.try pDoubleQuotted) pLitNum :: P.Parser Literal pLitNum = LitNum <$> (P.try (P.string "-0" $> (0 :: Scientific)) -- edge case <|> P.try pDoubleScientific <|> P.try pScientific) pLitBool :: P.Parser Literal pLitBool = LitBool <$> (P.try (P.string "true" $> True) <|> P.try (P.string "false" $> False)) pLitNull :: P.Parser Literal pLitNull = P.string "null" $> LitNull pCompSQ :: P.Parser Comparable pCompSQ = CompSQ <$> (P.try pCurrentSingleQ <|> P.try pRootSingleQ) pCurrentSingleQ :: P.Parser SingularQuery pCurrentSingleQ = do P.char '@' segs <- P.many $ P.try (pSpaces *> pSingularQuerySegment) return $ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = segs } pRootSingleQ :: P.Parser SingularQuery pRootSingleQ = do P.char '$' segs <- P.many $ P.try (pSpaces *> pSingularQuerySegment) return $ SingularQuery { singularQueryType = RootSQ, singularQuerySegments = segs } pSingularQuerySegment :: P.Parser SingularQuerySegment pSingularQuerySegment = P.try pSingularQNameSeg <|> P.try pSingularQIndexSeg pSingularQNameSeg :: P.Parser SingularQuerySegment pSingularQNameSeg = P.try pSingularQNameBracketed <|> P.try pSingularQNameDotted where pSingularQNameBracketed = do P.char '[' name <- T.pack <$> (P.try pSingleQuotted <|> P.try pDoubleQuotted) P.char ']' return $ NameSQSeg name pSingularQNameDotted = do P.char '.' P.lookAhead (P.letter <|> P.oneOf "_" <|> pUnicodeChar) name <- T.pack <$> P.many1 (P.alphaNum <|> P.oneOf "_" <|> pUnicodeChar) return $ NameSQSeg name pSingularQIndexSeg :: P.Parser SingularQuerySegment pSingularQIndexSeg = do P.char '[' idx <- pSignedInt P.char ']' return $ IndexSQSeg idx aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser/Name.hs0000644000000000000000000000543307346545000021544 0ustar0000000000000000{-# OPTIONS_GHC -Wno-unused-do-bind #-} module Data.Aeson.JSONPath.Parser.Name ( pSingleQuotted , pDoubleQuotted ) where import qualified Text.ParserCombinators.Parsec as P import Data.Functor (($>)) import Data.Char (ord, chr) import Text.ParserCombinators.Parsec ((<|>)) import Prelude pSingleQuotted :: P.Parser String pSingleQuotted = P.char '\'' *> P.many inQuote <* P.char '\'' where inQuote = P.try pUnescaped <|> P.try (P.char '\"') <|> P.try (P.string "\\\'" $> '\'') <|> P.try pEscaped pDoubleQuotted :: P.Parser String pDoubleQuotted = P.char '\"' *> P.many inQuote <* P.char '\"' where inQuote = P.try pUnescaped <|> P.try (P.char '\'') <|> P.try (P.string "\\\"" $> '\"') <|> P.try pEscaped pUnescaped :: P.Parser Char pUnescaped = P.satisfy inRange where inRange c = let code = ord c in (code >= 0x20 && code <= 0x21) || (code >= 0x23 && code <= 0x26) || (code >= 0x28 && code <= 0x5B) || (code >= 0x5D && code <= 0xD7FF) || (code >= 0xE000 && code <= 0x10FFFF) pEscaped :: P.Parser Char pEscaped = do P.char '\\' P.try pEscapees <|> P.try pHexUnicode where pEscapees = P.try (P.char 'b' $> '\b') <|> P.try (P.char 'f' $> '\f') <|> P.try (P.char 'n' $> '\n') <|> P.try (P.char 'r' $> '\r') <|> P.try (P.char 't' $> '\t') <|> P.try (P.char '/') <|> P.try (P.char '\\') pHexUnicode :: P.Parser Char pHexUnicode = P.try pNonSurrogate <|> P.try pSurrogatePair where pNonSurrogate = P.try pNonSurrogateFirst <|> P.try pNonSurrogateSecond where pNonSurrogateFirst = do P.char 'u' c1 <- P.digit <|> P.oneOf "AaBbCcEeFf" c2 <- P.hexDigit c3 <- P.hexDigit c4 <- P.hexDigit return $ chr (read ("0x" ++ [c1,c2,c3,c4]) :: Int) pNonSurrogateSecond = do P.char 'u' c1 <- P.oneOf "Dd" c2 <- P.oneOf "01234567" c3 <- P.hexDigit c4 <- P.hexDigit return $ chr (read ("0x" ++ [c1,c2,c3,c4]) :: Int) pSurrogatePair = do P.char 'u' high <- pHighSurrogate P.char '\\' P.char 'u' fromHighAndLow high <$> pLowSurrogate where pHighSurrogate = do c1 <- P.oneOf "Dd" c2 <- P.oneOf "89AaBb" c3 <- P.hexDigit c4 <- P.hexDigit return (read ("0x" ++ [c1,c2,c3,c4]) :: Int) pLowSurrogate = do c1 <- P.oneOf "Dd" c2 <- P.oneOf "CcDdEeFf" c3 <- P.hexDigit c4 <- P.hexDigit return (read ("0x" ++ [c1,c2,c3,c4]) :: Int) fromHighAndLow hi lo = chr $ ((hi - 0xD800) * 0x400) + (lo - 0xDC00) + 0x10000 aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser/Number.hs0000644000000000000000000000331507346545000022111 0ustar0000000000000000{-# OPTIONS_GHC -Wno-unused-do-bind #-} module Data.Aeson.JSONPath.Parser.Number ( pSignedInt , pScientific , pDoubleScientific ) where import qualified Text.ParserCombinators.Parsec as P import Data.Maybe (fromMaybe) import Data.Scientific (Scientific, scientific) import GHC.Num (integerFromInt, integerToInt) import Prelude pSignedInt :: P.Parser Int pSignedInt = do P.notFollowedBy (P.string "-0" *> P.optional P.digit) -- no leading -011... etc P.notFollowedBy (P.char '0' *> P.digit) -- no leading 011... etc sign <- P.optionMaybe $ P.char '-' num <- (read <$> P.many1 P.digit) :: P.Parser Integer checkNumOutOfRange num sign where minInt = -9007199254740991 maxInt = 9007199254740991 checkNumOutOfRange num (Just _) = if -num < minInt then fail "out of range" else return $ integerToInt (-num) checkNumOutOfRange num Nothing = if num > maxInt then fail "out of range" else return $ integerToInt num -- TODO: Fix Double parse error "1.12e+23" pScientific :: P.Parser Scientific pScientific = do mantissa <- pSignedInt expo <- P.optionMaybe (P.oneOf "eE" *> pExponent) return $ scientific (integerFromInt mantissa) (fromMaybe 0 expo) pDoubleScientific :: P.Parser Scientific pDoubleScientific = do whole <- P.many1 P.digit P.char '.' frac <- P.many1 P.digit expo <- P.optionMaybe (P.oneOf "eE" *> pExponent) let num = read (whole ++ "." ++ frac ++ maybe "" (\x -> "e" ++ show x) expo) :: Scientific return num pExponent :: P.Parser Int pExponent = do sign <- P.optionMaybe (P.oneOf "+-") num <- read <$> P.many1 P.digit return $ case sign of Just '-' -> -num _ -> num aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Parser/Query.hs0000644000000000000000000000627507346545000021776 0ustar0000000000000000{-# OPTIONS_GHC -Wno-unused-do-bind #-} module Data.Aeson.JSONPath.Parser.Query ( pRootQuery , pCurrentQuery ) where import qualified Data.Text as T import qualified Text.ParserCombinators.Parsec as P import Data.Functor (($>)) import Data.Maybe (isNothing) import Text.ParserCombinators.Parsec ((<|>)) import Data.Aeson.JSONPath.Parser.Filter (pFilter) import Data.Aeson.JSONPath.Parser.Name import Data.Aeson.JSONPath.Parser.Number import Data.Aeson.JSONPath.Parser.Common import Data.Aeson.JSONPath.Types import Prelude pRootQuery :: P.Parser Query pRootQuery = do P.char '$' segs <- P.many $ P.try pSpacedOutSegments return $ Query { queryType = Root, querySegments = segs } where pQ = P.try pRootQuery <|> P.try pCurrentQuery pSpacedOutSegments = pSpaces *> pQuerySegment pQ pCurrentQuery :: P.Parser Query pCurrentQuery = do P.char '@' segs <- P.many $ P.try pSpacedOutSegments return $ Query { queryType = Current, querySegments = segs } where pQ = P.try pRootQuery <|> P.try pCurrentQuery pSpacedOutSegments = pSpaces *> pQuerySegment pQ pQuerySegment :: P.Parser a -> P.Parser (QuerySegment a) pQuerySegment pQ = do dotdot <- P.optionMaybe (P.try $ P.string "..") seg <- pSegment pQ $ isNothing dotdot let segType = if isNothing dotdot then Child else Descendant return $ QuerySegment { segmentType = segType, segment = seg } pSegment :: P.Parser a -> Bool -> P.Parser (Segment a) pSegment pQ isChild = P.try (pBracketed pQ) <|> P.try (pDotted isChild) <|> P.try (pWildcardSeg isChild) pBracketed :: P.Parser a -> P.Parser (Segment a) pBracketed pQ = do P.char '[' pSpaces sel <- pSelector pQ optionalSels <- P.many $ pCommaSepSelectors pQ pSpaces P.char ']' return $ Bracketed (sel:optionalSels) where pCommaSepSelectors :: P.Parser a -> P.Parser (Selector a) pCommaSepSelectors p = P.try $ pSpaces *> P.char ',' *> pSpaces *> pSelector p pDotted :: Bool -> P.Parser (Segment a) pDotted isChild = do (if isChild then P.string "." else P.string "") P.lookAhead (P.letter <|> P.oneOf "_" <|> pUnicodeChar) key <- T.pack <$> P.many1 (P.alphaNum <|> P.oneOf "_" <|> pUnicodeChar) return $ Dotted key pWildcardSeg :: Bool -> P.Parser (Segment a) pWildcardSeg isChild = (if isChild then P.string "." else P.string "") *> P.char '*' $> WildcardSegment pSelector :: P.Parser a -> P.Parser (Selector a) pSelector pQ = P.try pName <|> P.try pSlice <|> P.try pIndex <|> P.try pWildcardSel <|> P.try (pFilter pQ) pName :: P.Parser (Selector a) pName = Name . T.pack <$> (P.try pSingleQuotted <|> P.try pDoubleQuotted) pIndex :: P.Parser (Selector a) pIndex = Index <$> pSignedInt pSlice :: P.Parser (Selector a) pSlice = do start <- P.optionMaybe (pSignedInt <* pSpaces) P.char ':' pSpaces end <- P.optionMaybe (pSignedInt <* pSpaces) step <- P.optionMaybe (P.char ':' *> P.optionMaybe (pSpaces *> pSignedInt)) return $ ArraySlice (start, end, case step of Just (Just n) -> n _ -> 1) pWildcardSel :: P.Parser (Selector a) pWildcardSel = P.char '*' $> WildcardSelector aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Query.hs0000644000000000000000000000071007346545000020526 0ustar0000000000000000{- | Module : Data.Aeson.JSONPath.Query Description : Execute JSONPath Query after parsing Copyright : (c) 2024-2025 Taimoor Zaeem License : MIT Maintainer : Taimoor Zaeem Stability : Experimental Portability : Portable This module is responsible for executing the JSONPath query -} module Data.Aeson.JSONPath.Query ( module Data.Aeson.JSONPath.Query.Query ) where import Data.Aeson.JSONPath.Query.Query aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Query/0000755000000000000000000000000007346545000020174 5ustar0000000000000000aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Query/Filter.hs0000644000000000000000000000765007346545000021765 0ustar0000000000000000{-# LANGUAGE RecordWildCards #-} module Data.Aeson.JSONPath.Query.Filter ( filterOrExpr , filterOrExprLocated ) where import Data.Aeson (Value) import Data.Vector (Vector) import qualified Data.Aeson as JSON import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.Key as K import qualified Data.Vector as V import Data.Aeson.JSONPath.Types import Prelude filterOrExpr :: LogicalOrExpr Query -> QueryState -> Vector Value filterOrExpr expr qS@QueryState{ curVal=(JSON.Object obj) } = V.filter (\cur -> evaluateLogicalOrExpr expr qS{ curVal=cur}) (V.fromList $ KM.elems obj) filterOrExpr expr qS@QueryState{ curVal=(JSON.Array arr) } = V.filter (\cur -> evaluateLogicalOrExpr expr qS{ curVal=cur }) arr filterOrExpr _ _ = V.empty filterOrExprLocated :: LogicalOrExpr Query -> QueryState -> String -> Vector (String,Value) filterOrExprLocated expr qS@QueryState{ curVal=(JSON.Object obj) } loc = V.filter (\(_,x) -> evaluateLogicalOrExpr expr qS{ curVal=x}) (V.fromList $ zip locsWithKeys (KM.elems obj)) where locsWithKeys = map (\x -> loc ++ "['" ++ K.toString x ++ "']") (KM.keys obj) filterOrExprLocated expr qS@QueryState{ curVal=(JSON.Array arr) } loc = V.filter (\(_,x) -> evaluateLogicalOrExpr expr qS{ curVal=x}) (V.zip (V.fromList locsWithIdxs) arr) where locsWithIdxs = map (\x -> loc ++ "[" ++ show x ++ "]") [0..(V.length arr - 1)] filterOrExprLocated _ _ _ = V.empty evaluateLogicalOrExpr :: LogicalOrExpr Query -> QueryState -> Bool evaluateLogicalOrExpr (LogicalOr exprs) qS = any (`evaluateLogicalAndExpr` qS) exprs evaluateLogicalAndExpr :: LogicalAndExpr Query -> QueryState -> Bool evaluateLogicalAndExpr (LogicalAnd exprs) qS = all (`evaluateBasicExpr` qS) exprs evaluateBasicExpr :: BasicExpr Query -> QueryState -> Bool evaluateBasicExpr (Paren expr) qS = evaluateLogicalOrExpr expr qS evaluateBasicExpr (NotParen expr) qS = not $ evaluateLogicalOrExpr expr qS evaluateBasicExpr (Test expr) qS = evaluateTestExpr expr qS evaluateBasicExpr (NotTest expr) qS = not $ evaluateTestExpr expr qS evaluateBasicExpr (Comparison expr) qS = evaluateCompExpr expr qS evaluateTestExpr :: TestExpr Query -> QueryState -> Bool evaluateTestExpr (FilterQuery expr) qS@QueryState{..} = not $ null $ executeQuery expr qS evaluateCompExpr :: ComparisonExpr -> QueryState -> Bool evaluateCompExpr (Comp leftC op rightC) qS = compareVals op (getComparableVal leftC qS) (getComparableVal rightC qS) compareVals :: ComparisonOp -> Maybe Value -> Maybe Value -> Bool compareVals Less (Just (JSON.String s1)) (Just (JSON.String s2)) = s1 < s2 compareVals Less (Just (JSON.Number n1)) (Just (JSON.Number n2)) = n1 < n2 compareVals Less _ _ = False compareVals LessOrEqual o1 o2 = compareVals Less o1 o2 || compareVals Equal o1 o2 compareVals Greater o1 o2 = compareVals Less o2 o1 compareVals GreaterOrEqual o1 o2 = compareVals Less o2 o1 || compareVals Equal o1 o2 compareVals Equal o1 o2 = o1 == o2 compareVals NotEqual o1 o2 = o1 /= o2 getComparableVal :: Comparable -> QueryState -> Maybe Value getComparableVal (CompLit lit) _ = case lit of LitString txt -> Just $ JSON.String txt LitNum num -> Just $ JSON.Number num LitBool bool -> Just $ JSON.Bool bool LitNull -> Just JSON.Null getComparableVal (CompSQ SingularQuery{..}) QueryState{..} = case singularQueryType of RootSQ -> traverseSingularQSegs (Just rootVal) singularQuerySegments CurrentSQ -> traverseSingularQSegs (Just curVal) singularQuerySegments traverseSingularQSegs :: Maybe Value -> [SingularQuerySegment] -> Maybe Value traverseSingularQSegs = foldl lookupSingleQSeg lookupSingleQSeg :: Maybe Value -> SingularQuerySegment -> Maybe Value lookupSingleQSeg (Just (JSON.Object obj)) (NameSQSeg txt) = KM.lookup (K.fromText txt) obj lookupSingleQSeg (Just (JSON.Array arr)) (IndexSQSeg idx) = (V.!?) arr idx lookupSingleQSeg _ _ = Nothing aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Query/Query.hs0000644000000000000000000000254707346545000021645 0ustar0000000000000000{-# LANGUAGE RecordWildCards #-} module Data.Aeson.JSONPath.Query.Query ( qQuery , qQueryLocated ) where import Control.Monad (join) import Data.Aeson (Value) import Data.Vector (Vector) import qualified Data.Vector as V import Data.Aeson.JSONPath.Types import Data.Aeson.JSONPath.Query.Segment import Prelude -- | Runs the JSONPath Query qQuery :: Query -> QueryState -> Vector Value qQuery Query{..} qS@QueryState{..} = case queryType of Root -> foldl applySegment (V.singleton rootVal) querySegments Current -> foldl applySegment (V.singleton curVal) querySegments where applySegment :: Vector Value -> QuerySegment Query -> Vector Value applySegment vec seg = join $ V.map (\x -> qQuerySegment seg qS{ curVal = x }) vec -- | Runs the JSONPath Query also returning node locations qQueryLocated :: Query -> QueryState -> String -> Vector (String,Value) qQueryLocated Query{..} qS@QueryState{..} loc = case queryType of Root -> foldl applySegment (V.singleton (loc,rootVal)) querySegments Current -> foldl applySegment (V.singleton (loc,curVal)) querySegments where applySegment :: Vector (String,Value) -> QuerySegment Query -> Vector (String,Value) applySegment vec seg = join $ V.map (\(location,cur) -> qQuerySegmentLocated seg qS{ curVal = cur } location) vec aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Query/Segment.hs0000644000000000000000000000663207346545000022141 0ustar0000000000000000{-# LANGUAGE RecordWildCards #-} module Data.Aeson.JSONPath.Query.Segment ( qQuerySegment , qQuerySegmentLocated ) where import Control.Monad (join) import Data.Aeson (Value) import Data.Vector (Vector) import qualified Data.Aeson as JSON import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.Key as K import qualified Data.Vector as V import Data.Aeson.JSONPath.Types import Data.Aeson.JSONPath.Query.Selector import Prelude qQuerySegment :: QuerySegment Query -> QueryState -> Vector Value qQuerySegment QuerySegment{..} qS@QueryState{..} = case segmentType of Child -> joinAfterMap $ V.singleton curVal Descendant -> joinAfterMap $ allElemsRecursive curVal where joinAfterMap vec = join $ V.map (\cur -> qSegment segment qS{ curVal = cur }) vec qQuerySegmentLocated :: QuerySegment Query -> QueryState-> String -> Vector (String, Value) qQuerySegmentLocated QuerySegment{..} qS@QueryState{..} loc = case segmentType of Child -> joinAfterMap $ V.singleton (loc,curVal) Descendant -> joinAfterMap $ allElemsRecursiveLocated (loc,curVal) where joinAfterMap vec = join $ V.map (\(location,cur) -> qSegmentLocated segment qS{ curVal = cur } location) vec qSegment :: Segment Query -> QueryState -> Vector Value qSegment (Bracketed sels) qS = V.concat $ map (`qSelector` qS) sels qSegment (Dotted key) qS = qSelector (Name key) qS qSegment WildcardSegment qS = qSelector WildcardSelector qS qSegmentLocated :: Segment Query -> QueryState -> String -> Vector (String,Value) qSegmentLocated (Bracketed sels) qS loc = V.concat $ map (\sel -> qSelectorLocated sel qS loc) sels qSegmentLocated (Dotted key) qS loc = qSelectorLocated (Name key) qS loc qSegmentLocated WildcardSegment qS loc = qSelectorLocated WildcardSelector qS loc -- TODO: Looks kinda ugly, make it pretty <3 allElemsRecursive :: Value -> Vector Value allElemsRecursive o@(JSON.Object obj) = V.concat [ V.singleton o, V.concat $ map allElemsRecursive (KM.elems obj) ] allElemsRecursive a@(JSON.Array arr) = V.concat [ V.singleton a, V.concat $ map allElemsRecursive (V.toList arr) ] allElemsRecursive _ = V.empty -- TODO: Looks kinda ugly, make it pretty <3 allElemsRecursiveLocated :: (String,Value) -> Vector (String,Value) allElemsRecursiveLocated (loc, o@(JSON.Object obj)) = V.concat [ V.singleton (loc,o), V.concat $ zipWith (curry allElemsRecursiveLocated) keys (KM.elems obj) ] where keys = map (toPathKey loc) $ KM.keys obj allElemsRecursiveLocated (loc, a@(JSON.Array arr)) = V.concat [ V.singleton (loc,a), V.concat $ zipWith (curry allElemsRecursiveLocated) indices (V.toList arr) ] where indices = map (toPathIdx loc) [0..V.length arr - 1] allElemsRecursiveLocated _ = V.empty toPathKey :: String -> KM.Key -> String toPathKey loc key = loc ++ "['" ++ escapeEscapees (K.toString key) ++ "']" where escapeEscapees :: String -> String escapeEscapees [] = [] escapeEscapees (x:xs) = checkChar x ++ escapeEscapees xs where -- TODO: Do we need to escape unicode chars? checkChar '\\' = ['\\', '\\'] checkChar '\'' = ['\\', '\''] checkChar '\b' = ['\\', 'b'] checkChar '\r' = ['\\', 'r'] checkChar '\t' = ['\\', 't'] checkChar '\f' = ['\\', 'f'] checkChar '\n' = ['\\', 'n'] checkChar c = [c] toPathIdx :: String -> Int -> String toPathIdx loc idx = loc ++ "[" ++ show idx ++ "]" aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Query/Selector.hs0000644000000000000000000001252207346545000022312 0ustar0000000000000000{-# LANGUAGE RecordWildCards #-} module Data.Aeson.JSONPath.Query.Selector ( qSelector , qSelectorLocated ) where import Data.Aeson (Value) import Data.Vector (Vector) import Data.Text (Text) import qualified Data.Aeson as JSON import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.Key as K import qualified Data.Text as T import qualified Data.Vector as V import Data.Aeson.JSONPath.Types import Data.Aeson.JSONPath.Query.Filter import Prelude qSelector :: Selector Query -> QueryState -> Vector Value qSelector (Name key) QueryState{curVal=(JSON.Object obj)} = maybe V.empty V.singleton $ KM.lookup (K.fromText key) obj qSelector (Name _) _ = V.empty qSelector (Index idx) QueryState{curVal=(JSON.Array arr)} = maybe V.empty V.singleton $ if idx >= 0 then (V.!?) arr idx else (V.!?) arr (idx + V.length arr) qSelector (Index _) _ = V.empty qSelector (ArraySlice startEndStep) QueryState{curVal=(JSON.Array arr) } = sliceArray startEndStep arr qSelector (ArraySlice _) _ = V.empty qSelector (Filter orExpr) qS = filterOrExpr orExpr qS qSelector WildcardSelector QueryState{..} = case curVal of (JSON.Object obj) -> V.fromList $ KM.elems obj (JSON.Array arr) -> arr _ -> V.empty qSelectorLocated :: Selector Query -> QueryState -> String -> Vector (String,Value) qSelectorLocated (Name key) QueryState{curVal=(JSON.Object obj)} loc = maybe V.empty (\x-> V.singleton (toPathKey loc key, x)) $ KM.lookup (K.fromText key) obj qSelectorLocated (Name _) _ _ = V.empty qSelectorLocated (Index idx) QueryState{curVal=(JSON.Array arr)} loc = maybe V.empty (\x-> V.singleton (newLocation, x)) $ (V.!?) arr (getIndex idx) where newLocation = loc ++ "[" ++ show (getIndex idx) ++ "]" getIndex i = if i >= 0 then i else i + V.length arr qSelectorLocated (Index _) _ _ = V.empty qSelectorLocated (ArraySlice (start,end,step)) QueryState{curVal=(JSON.Array arr)} loc = sliceArrayLocated (start,end,step) $ V.zip (V.fromList locs) arr where locs = [ loc ++ "[" ++ show i ++ "]" | i <- indices ] indices = [0..(V.length arr - 1)] qSelectorLocated (ArraySlice _) _ _ = V.empty qSelectorLocated (Filter orExpr) qS loc = filterOrExprLocated orExpr qS loc qSelectorLocated WildcardSelector QueryState{..} loc = case curVal of (JSON.Object obj) -> V.fromList $ zip (locsWithKeys obj) (KM.elems obj) (JSON.Array arr) -> V.zip (V.fromList (locsWithIdxs arr)) arr _ -> V.empty where locsWithKeys obj = map (toPathKey loc . K.toText) (KM.keys obj) locsWithIdxs arr = map (\x -> loc ++ "[" ++ show x ++ "]") [0..(V.length arr)] sliceArray :: (Maybe Int, Maybe Int, Int) -> Vector Value -> Vector Value sliceArray (start,end,step) vec = case compare step 0 of GT -> getSliceForward (maybe 0 normalize start) (maybe len normalize end) step vec LT -> getSliceReverse (maybe (len-1) normalize start) (maybe (-1) normalize end) step vec EQ -> V.empty where -- TODO: Looks kinda ugly, make it pretty <3 len = V.length vec normalize i = if i >= 0 then i else len + i getSliceForward st en stp arr = loop lower V.empty where (lower,upper) = (min (max st 0) len, min (max en 0) len) loop i acc = if i < upper then loop (i+stp) $ V.snoc acc $ (V.!) arr (normalize i) else acc getSliceReverse st en stp arr = loop upper V.empty where (lower,upper) = (min (max en (-1)) (len-1), min (max st (-1)) (len-1)) loop i acc = if lower < i then loop (i+stp) $ V.snoc acc $ (V.!) arr (normalize i) else acc sliceArrayLocated :: (Maybe Int, Maybe Int, Int) -> Vector (String,Value) -> Vector (String,Value) sliceArrayLocated (start,end,step) vec = case compare step 0 of GT -> getSliceForward (maybe 0 normalize start) (maybe len normalize end) step vec LT -> getSliceReverse (maybe (len-1) normalize start) (maybe (-1) normalize end) step vec EQ -> V.empty where -- TODO: Looks kinda ugly, make it pretty <3 len = V.length vec normalize i = if i >= 0 then i else len + i getSliceForward st en stp arr = loop lower V.empty where (lower,upper) = (min (max st 0) len, min (max en 0) len) loop i acc = if i < upper then loop (i+stp) $ V.snoc acc $ (V.!) arr (normalize i) else acc getSliceReverse st en stp arr = loop upper V.empty where (lower,upper) = (min (max en (-1)) (len-1), min (max st (-1)) (len-1)) loop i acc = if lower < i then loop (i+stp) $ V.snoc acc $ (V.!) arr (normalize i) else acc toPathKey :: String -> Text -> String toPathKey loc key = loc ++ "['" ++ escapeEscapees (T.unpack key) ++ "']" where escapeEscapees :: String -> String escapeEscapees [] = [] escapeEscapees (x:xs) = checkChar x ++ escapeEscapees xs where -- TODO: Do we need to escape unicode chars? checkChar '\\' = ['\\', '\\'] checkChar '\'' = ['\\', '\''] checkChar '\b' = ['\\', 'b'] checkChar '\r' = ['\\', 'r'] checkChar '\t' = ['\\', 't'] checkChar '\f' = ['\\', 'f'] checkChar '\n' = ['\\', 'n'] checkChar c = [c] aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Types.hs0000644000000000000000000000132407346545000020527 0ustar0000000000000000{- | Module : Data.Aeson.JSONPath.Types Description : Types related to JSONPath elements Copyright : (c) 2024-2025 Taimoor Zaeem License : MIT Maintainer : Taimoor Zaeem Stability : Experimental Portability : Portable This module contains all the data structures related to JSONPath -} module Data.Aeson.JSONPath.Types ( module Data.Aeson.JSONPath.Types.Query , module Data.Aeson.JSONPath.Types.Segment , module Data.Aeson.JSONPath.Types.Selector , module Data.Aeson.JSONPath.Types.Filter ) where import Data.Aeson.JSONPath.Types.Query import Data.Aeson.JSONPath.Types.Segment import Data.Aeson.JSONPath.Types.Selector import Data.Aeson.JSONPath.Types.Filter aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Types/0000755000000000000000000000000007346545000020173 5ustar0000000000000000aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Types/Filter.hs0000644000000000000000000000332407346545000021756 0ustar0000000000000000{-# LANGUAGE DeriveLift #-} module Data.Aeson.JSONPath.Types.Filter (LogicalOrExpr (..) , LogicalAndExpr (..) , BasicExpr (..) , TestExpr (..) , ComparisonExpr (..) , ComparisonOp (..) , Comparable (..) , Literal (..) , SingularQueryType (..) , SingularQuery (..) , SingularQuerySegment (..) ) where import Data.Text (Text) import Data.Scientific (Scientific) import Language.Haskell.TH.Syntax (Lift) import Prelude -- | newtype LogicalOrExpr a = LogicalOr [LogicalAndExpr a] deriving (Eq, Show, Lift) -- | newtype LogicalAndExpr a = LogicalAnd [BasicExpr a] deriving (Eq, Show, Lift) -- | data BasicExpr a = Paren (LogicalOrExpr a) -- ( expr ) | NotParen (LogicalOrExpr a) -- not (expr) | Test (TestExpr a) -- query | NotTest (TestExpr a) -- not query | Comparison ComparisonExpr deriving (Eq, Show, Lift) -- | newtype TestExpr a = FilterQuery a deriving (Eq, Show, Lift) -- | data ComparisonExpr = Comp Comparable ComparisonOp Comparable deriving (Eq, Show, Lift) -- | data ComparisonOp = Less | LessOrEqual | Greater | GreaterOrEqual | Equal | NotEqual deriving (Eq, Show, Lift) -- | data Comparable = CompLit Literal | CompSQ SingularQuery deriving (Eq, Show, Lift) -- | data Literal = LitString Text | LitNum Scientific | LitBool Bool | LitNull deriving (Eq, Show, Lift) -- | data SingularQueryType = RootSQ | CurrentSQ deriving (Eq, Show, Lift) -- | data SingularQuery = SingularQuery { singularQueryType :: SingularQueryType , singularQuerySegments :: [SingularQuerySegment] } deriving (Eq, Show, Lift) -- | data SingularQuerySegment = NameSQSeg Text | IndexSQSeg Int deriving (Eq, Show, Lift) aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Types/Query.hs0000644000000000000000000000122607346545000021635 0ustar0000000000000000{-# LANGUAGE DeriveLift #-} module Data.Aeson.JSONPath.Types.Query ( Query (..) , QueryType (..) , QueryState (..) ) where import Data.Aeson (Value) import Data.Vector (Vector) import Data.Aeson.JSONPath.Types.Segment (QuerySegment (..)) import Language.Haskell.TH.Syntax (Lift) import Prelude -- | data QueryState = QueryState { rootVal :: Value , curVal :: Value , executeQuery :: Query -> QueryState -> Vector Value } -- | data QueryType = Root | Current deriving (Eq, Show, Lift) -- | data Query = Query { queryType :: QueryType , querySegments :: [QuerySegment Query] } deriving (Eq, Show, Lift) aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Types/Segment.hs0000644000000000000000000000117007346545000022130 0ustar0000000000000000{-# LANGUAGE DeriveLift #-} module Data.Aeson.JSONPath.Types.Segment (Segment (..) , QuerySegment (..) , SegmentType (..) ) where import Data.Aeson.JSONPath.Types.Selector (Selector (..)) import Data.Text (Text) import Language.Haskell.TH.Syntax (Lift) import Prelude -- | data SegmentType = Child | Descendant deriving (Eq, Show, Lift) -- | data QuerySegment a = QuerySegment { segmentType :: SegmentType , segment :: Segment a } deriving (Eq, Show, Lift) -- | data Segment a = Bracketed [Selector a] | Dotted Text | WildcardSegment deriving (Eq, Show, Lift) aeson-jsonpath-0.3.0.2/src/Data/Aeson/JSONPath/Types/Selector.hs0000644000000000000000000000066707346545000022320 0ustar0000000000000000{-# LANGUAGE DeriveLift #-} module Data.Aeson.JSONPath.Types.Selector (Selector (..)) where import Data.Aeson.JSONPath.Types.Filter (LogicalOrExpr (..)) import Data.Text (Text) import Language.Haskell.TH.Syntax (Lift) import Prelude -- | data Selector a = Name Text | Index Int | ArraySlice (Maybe Int, Maybe Int, Int) | Filter (LogicalOrExpr a) | WildcardSelector deriving (Eq, Show, Lift) aeson-jsonpath-0.3.0.2/test/compliance/0000755000000000000000000000000007346545000016065 5ustar0000000000000000aeson-jsonpath-0.3.0.2/test/compliance/ComplianceSpec.hs0000644000000000000000000000517307346545000021314 0ustar0000000000000000{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NamedFieldPuns #-} module ComplianceSpec ( spec , TestSuite (..)) where import qualified Data.Aeson as JSON import qualified Data.Vector as V import qualified Text.ParserCombinators.Parsec as P import Control.Monad (mzero) import Data.Aeson ((.:),(.:?),Value) import Data.Aeson.JSONPath (query, queryLocated) import Data.Aeson.JSONPath.Parser (pQuery) import Data.Either (isLeft, fromRight) import Data.Vector (Vector) import Test.Hspec import Prelude newtype TestSuite = TestSuite { tests :: [TestCase] } deriving (Show) instance JSON.FromJSON TestSuite where parseJSON (JSON.Object o) = TestSuite <$> o .: "tests" parseJSON _ = mzero data TestCase = TestCase { name :: String , selector :: String , document :: Maybe Value , result :: Maybe (Vector Value) , results :: Maybe [Vector Value] , resultPaths :: Maybe (Vector String) , resultsPaths :: Maybe [Vector String] , invalidSel :: Maybe Bool , tags :: Maybe [String] } deriving (Show) instance JSON.FromJSON TestCase where parseJSON (JSON.Object o) = TestCase <$> o .: "name" <*> o .: "selector" <*> o .:? "document" <*> o .:? "result" <*> o .:? "results" <*> o .:? "result_paths" <*> o .:? "results_paths" <*> o .:? "invalid_selector" <*> o .:? "tags" parseJSON _ = mzero spec :: TestSuite -> Spec spec TestSuite{tests} = do describe "compliance tests" $ do mapM_ runTestCase tests runTestCase :: TestCase -> SpecWith () -- skip function extension tests runTestCase tc@TestCase{tags=Just xs, ..} = if "function" `elem` xs then xit name pending else runTestCase tc{tags=Nothing} -- invalid selector should not parse correctly runTestCase TestCase{invalidSel=(Just True), ..} = it name $ P.parse pQuery "" selector `shouldSatisfy` isLeft -- if result is deterministic (one json) runTestCase TestCase{result=(Just r), resultPaths=(Just rp), document=(Just doc), ..} = it name $ do query selector doc `shouldBe` Right r queryLocated selector doc `shouldBe` Right (V.zip rp r) -- if result is non-deterministic (any json from the list of results) runTestCase TestCase{results=(Just rs), resultsPaths=(Just rsp), document=(Just doc), ..} = do it name $ do query selector doc `shouldSatisfy` (\x -> fromRight V.empty x `elem` rs) queryLocated selector doc `shouldSatisfy` (\x -> fromRight V.empty x `elem` vecList) where vecList = [ V.zip rp r | rp <- rsp, r <- rs] runTestCase _ = pure () aeson-jsonpath-0.3.0.2/test/compliance/Main.hs0000644000000000000000000000213207346545000017303 0ustar0000000000000000module Main ( main ) where import qualified Data.Aeson as JSON import qualified Test.Hspec as HS import qualified Data.Text.Encoding as T import qualified Data.Text.IO as TIO import Data.Either (fromRight) import qualified ComplianceSpec import qualified Paths_aeson_jsonpath as Paths import ComplianceSpec (TestSuite (..)) import Test.Hspec.Runner import Prelude compliance :: Either String TestSuite -> Spec compliance cts = do HS.describe "Run compliance tests" $ ComplianceSpec.spec (fromRight TestSuite{tests=[]} cts) readCtsFile :: FilePath -> IO (Either String TestSuite) readCtsFile filePath = do contents <- TIO.readFile filePath return $ JSON.eitherDecodeStrict' $ T.encodeUtf8 contents main :: IO () main = do file <- Paths.getDataFileName "jsonpath-compliance-test-suite/cts.json" cts <- readCtsFile file summary <- hspecWithResult defaultConfig { configColorMode = ColorAuto } (compliance cts) putStrLn $ "Total tests: " ++ show (summaryExamples summary) putStrLn $ "Failures: " ++ show (summaryFailures summary) aeson-jsonpath-0.3.0.2/test/spec/0000755000000000000000000000000007346545000014705 5ustar0000000000000000aeson-jsonpath-0.3.0.2/test/spec/LocatedSpec.hs0000644000000000000000000000722307346545000017433 0ustar0000000000000000module LocatedSpec ( spec ) where import qualified Data.Aeson as JSON import qualified Data.Vector as V import Data.Aeson.JSONPath (queryLocatedQQ, jsonPath) import Data.Aeson.QQ.Simple (aesonQQ) import Data.Aeson (Value) import Data.Vector (Vector) import Test.Hspec import Prelude getVector :: Value -> Vector Value getVector (JSON.Array arr) = arr getVector _ = V.empty -- taken from https://serdejsonpath.live/ rootDoc :: Value rootDoc = [aesonQQ|{ "store": { "books": [ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ] } }|] storeDoc :: Value storeDoc = [aesonQQ| { "books": [ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ] }|] booksDoc :: Value booksDoc = [aesonQQ| [ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ]|] books0Doc :: Value books0Doc = [aesonQQ|[ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 } ]|] spec :: Spec spec = do describe "test queryLocated" $ do it "returns root document when query is $" $ queryLocatedQQ [jsonPath|$|] rootDoc `shouldBe` V.singleton ("$",rootDoc) it "returns store object when queryQQ is $.store" $ queryLocatedQQ [jsonPath|$.store|] rootDoc `shouldBe` V.fromList [("$['store']",storeDoc)] it "returns store object when queryQQ is $['store']" $ queryLocatedQQ [jsonPath|$['store']|] rootDoc `shouldBe` V.fromList [("$['store']",storeDoc)] it "returns books array when queryQQ is $.store.books" $ queryLocatedQQ [jsonPath|$.store.books|] rootDoc `shouldBe` V.fromList [("$['store']['books']",booksDoc)] it "returns 0-index book item, $.store.books[0]" $ queryLocatedQQ [jsonPath|$.store.books[0]|] rootDoc `shouldBe` V.zip (V.singleton "$['store']['books'][0]") (getVector books0Doc) it "returns 0-index book item, $.store.books[-4]" $ queryLocatedQQ [jsonPath|$.store.books[-4]|] rootDoc `shouldBe` V.zip (V.singleton "$['store']['books'][0]") (getVector books0Doc) aeson-jsonpath-0.3.0.2/test/spec/Main.hs0000644000000000000000000000105607346545000016127 0ustar0000000000000000module Main ( main ) where import qualified Test.Hspec as HS import qualified ParserSpec import qualified QuerySpec import qualified LocatedSpec import Test.Hspec.Runner import Prelude specs :: Spec specs = do HS.describe "Run all tests" $ do ParserSpec.spec QuerySpec.spec LocatedSpec.spec main :: IO () main = do summary <- hspecWithResult defaultConfig { configColorMode = ColorAuto } specs putStrLn $ "Total tests: " ++ show (summaryExamples summary) putStrLn $ "Failures: " ++ show (summaryFailures summary) aeson-jsonpath-0.3.0.2/test/spec/ParserSpec.hs0000644000000000000000000003324707346545000017321 0ustar0000000000000000module ParserSpec ( spec ) where import qualified Text.ParserCombinators.Parsec as P import Data.Aeson.JSONPath.Parser (pQuery) import Data.Either (isLeft) import Data.Aeson.JSONPath.Types import Test.Hspec import Prelude spec :: Spec spec = do describe "Parse query string" $ do it "parses query: $" $ P.parse pQuery "" "$" `shouldBe` Right (Query{queryType=Root,querySegments=[]}) it "parses query: $.store" $ P.parse pQuery "" "$.store" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }] } it "parses query: $['store']" $ P.parse pQuery "" "$['store']" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [Name "store"] }] } it "parses query: $.store.books" $ P.parse pQuery "" "$.store.books" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }] } it "parses query: $.store.books[0]" $ P.parse pQuery "" "$.store.books[0]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Index 0] }] } it "parses query: $.store.books[0,2]" $ P.parse pQuery "" "$.store.books[0,2]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Index 0, Index 2] }] } it "parses query: $.store.books[1:3]" $ P.parse pQuery "" "$.store.books[1:3]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [ArraySlice (Just 1, Just 3, 1)] }] } it "parses query: $.store.books[1:4:2]" $ P.parse pQuery "" "$.store.books[1:4:2]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [ArraySlice (Just 1, Just 4, 2)] }] } it "parses query: $.store.books[-1:-4:-2]" $ P.parse pQuery "" "$.store.books[-1:-4:-2]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [ArraySlice (Just (-1), Just (-4), (-2))] }] } it "parses query: $.store.books[1:3, 0, 1]" $ P.parse pQuery "" "$.store.books[1:3, 0, 1]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [ArraySlice (Just 1, Just 3, 1), Index 0, Index 1] }] } it "parses query: $.*" $ P.parse pQuery "" "$.*" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = WildcardSegment }] } it "parses query: $[*]" $ P.parse pQuery "" "$[*]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [WildcardSelector] }] } it "parses query: $..*" $ P.parse pQuery "" "$..*" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Descendant, segment = WildcardSegment }] } it "parses query: $..[*]" $ P.parse pQuery "" "$..[*]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Descendant, segment = Bracketed [WildcardSelector] }] } it "fails with $.1startsWithNum" $ P.parse pQuery "" "$.1startsWithNum" `shouldSatisfy` isLeft it "parses query $.©®±×÷Ωπ•€→∀∃∈≠≤≥✓λ" $ P.parse pQuery "" "$.©®±×÷Ωπ•€→∀∃∈≠≤≥✓λ" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "©®±×÷Ωπ•€→∀∃∈≠≤≥✓λ" }] } it "parses query: $._" $ P.parse pQuery "" "$._" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "_" }] } it "parses query: $.underscore_key" $ P.parse pQuery "" "$.underscore_key" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "underscore_key" }] } it "parses query: $[0,TAB/LINEFEED/RETURN/SPACE1]" $ P.parse pQuery "" "$[0,\n\t\r 1]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [Index 0, Index 1] }] } describe "parses JSPFilter Query" $ do it "$[?@.category == 'reference']" $ P.parse pQuery "" "$[?@.category == 'reference']" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "category"] }) Equal (CompLit (LitString "reference")))]])] }] } it "test expr: $..books[?@.price].title" $ P.parse pQuery "" "$..books[?@.price].title" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Descendant, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Test (FilterQuery Query { queryType = Current, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "price" }] })]])] }, QuerySegment { segmentType = Child, segment = Dotted "title" }] } it "and expr: $[?@.price < 20 && @.price > 10]" $ P.parse pQuery "" "$[?@.price < 20 && @.price > 10]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Less (CompLit (LitNum 20.0))), Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Greater (CompLit (LitNum 10.0)))]])] }] } it "or expr: $[?@.price < 20 || @.price > 10]" $ P.parse pQuery "" "$[?@.price < 20 || @.price > 10]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Less (CompLit (LitNum 20.0)))], LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Greater (CompLit (LitNum 10.0)))]])] }] } it "root filter: $..books[?$.price].title" $ P.parse pQuery "" "$..books[?$.price].title" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Descendant, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Test (FilterQuery Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "price" }] })]])] }, QuerySegment { segmentType = Child, segment = Dotted "title" }] } it "not expr: $[?!(@.price < 20 && @.price > 10)]" $ P.parse pQuery "" "$[?!(@.price < 20 && @.price > 10)]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [NotParen (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Less (CompLit (LitNum 20.0))), Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Greater (CompLit (LitNum 10.0)))]])]])] }] } it "scientific: $.store.books[?@.price < -1e20]" $ P.parse pQuery "" "$.store.books[?@['price'] < -1e20]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Less (CompLit (LitNum (-1.0e20))))]])] }] } it "double: $.store.books[?@.price < 0.01]" $ P.parse pQuery "" "$.store.books[?@['price'] < 0.01]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "price"] }) Less (CompLit (LitNum (1.0e-2))))]])] }] } it "bool: $.store.books[?@.is_available == true]" $ P.parse pQuery "" "$.store.books[?@.is_available == true]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "is_available"] }) Equal (CompLit (LitBool True)))]])] }] } it "null: $.store.books[?@.is_available == null]" $ P.parse pQuery "" "$.store.books[?@.is_available == null]" `shouldBe` Right Query { queryType = Root, querySegments = [QuerySegment { segmentType = Child, segment = Dotted "store" }, QuerySegment { segmentType = Child, segment = Dotted "books" }, QuerySegment { segmentType = Child, segment = Bracketed [Filter (LogicalOr [LogicalAnd [Comparison (Comp (CompSQ SingularQuery { singularQueryType = CurrentSQ, singularQuerySegments = [NameSQSeg "is_available"] }) Equal (CompLit LitNull))]])] }] } aeson-jsonpath-0.3.0.2/test/spec/QuerySpec.hs0000644000000000000000000002177107346545000017171 0ustar0000000000000000module QuerySpec ( spec ) where import qualified Data.Vector as V import qualified Data.Aeson as JSON import Data.Aeson.QQ.Simple (aesonQQ) import Data.Aeson (Value) import Data.Vector (Vector) import Test.Hspec import Data.Aeson.JSONPath (queryQQ, jsonPath) import Prelude getVector :: Value -> Vector Value getVector (JSON.Array arr) = arr getVector _ = V.empty -- taken from https://serdejsonpath.live/ rootDoc :: Value rootDoc = [aesonQQ|{ "store": { "books": [ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ] } }|] storeDoc :: Value storeDoc = [aesonQQ| { "books": [ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ] }|] booksDoc :: Value booksDoc = [aesonQQ| [ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ]|] books0Doc :: Value books0Doc = [aesonQQ|[ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 } ]|] books0And2Doc :: Value books0And2Doc = [aesonQQ|[ { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 } ]|] books1To3Doc :: Value books1To3Doc = [aesonQQ|[ { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 } ]|] books1To3And0And1Doc :: Value books1To3And0And1Doc = [aesonQQ|[ { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Guns, Germs, and Steel", "author": "Jared Diamond", "category": "reference", "price": 24.99 }, { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 } ]|] lessThanPrice20Books :: Value lessThanPrice20Books = [aesonQQ| [ { "title": "David Copperfield", "author": "Charles Dickens", "category": "fiction", "price": 12.99 }, { "title": "Moby Dick", "author": "Herman Melville", "category": "fiction", "price": 8.99 }, { "title": "Crime and Punishment", "author": "Fyodor Dostoevsky", "category": "fiction", "price": 19.99 } ]|] alphaArr :: Value alphaArr = [aesonQQ| ["a","b","c","d","e","f","g"] |] fgArr :: Value fgArr = [aesonQQ| ["f","g"] |] bdArr :: Value bdArr = [aesonQQ| ["b","d"] |] fdArr :: Value fdArr = [aesonQQ| ["f","d"] |] gfedcbaArr :: Value gfedcbaArr = [aesonQQ| ["g","f","e","d","c","b","a"] |] rfcExample1 :: Value rfcExample1 = [aesonQQ| { "o": {"j": 1, "k": 2}, "a": [5, 3, [{"j": 4}, {"k": 6}]] } |] rfcExample1Desc :: Value rfcExample1Desc = [aesonQQ| [ [5, 3, [{"j": 4}, {"k": 6}]], {"j": 1, "k": 2}, 5, 3, [{"j": 4}, {"k": 6}], {"j": 4}, {"k": 6}, 4, 6, 1, 2 ]|] boolsAndNulls :: Value boolsAndNulls = [aesonQQ| [ { "a": true }, { "a": false }, { "a": null } ]|] boolsAndNullsA :: Value boolsAndNullsA = [aesonQQ| [{ "a": true }] |] boolsAndNullsB :: Value boolsAndNullsB = [aesonQQ| [{ "a": false }] |] boolsAndNullsC :: Value boolsAndNullsC = [aesonQQ| [{ "a": null }] |] spec :: Spec spec = do describe "test query" $ do it "returns root document when query is $" $ queryQQ [jsonPath|$|] rootDoc `shouldBe` V.singleton rootDoc it "returns store object when queryQQ is $.store" $ queryQQ [jsonPath|$.store|] rootDoc `shouldBe` V.singleton storeDoc it "returns store object when queryQQ is $['store']" $ queryQQ [jsonPath|$['store']|] rootDoc `shouldBe` V.singleton storeDoc it "returns books array when queryQQ is $.store.books" $ queryQQ [jsonPath|$.store.books|] rootDoc `shouldBe` V.singleton booksDoc it "returns 0-index book item when queryQQ is $.store.books[0]" $ queryQQ [jsonPath|$.store.books[0]|] rootDoc `shouldBe` getVector books0Doc it "returns 0-index book item when queryQQ is $.store.books[-4]" $ queryQQ [jsonPath|$.store.books[-4]|] rootDoc `shouldBe` getVector books0Doc it "returns 0,2-index item when queryQQ is $.store.books[0,2]" $ queryQQ [jsonPath|$.store.books[0,2]|] rootDoc `shouldBe` getVector books0And2Doc it "returns 1To3-index when queryQQ is $.store.books[1:3]" $ queryQQ [jsonPath|$.store.books[1:3]|] rootDoc `shouldBe` getVector books1To3Doc it "returns 1To3-index and 0,1-index when queryQQ is $.store.books[1:3,0,1]" $ queryQQ [jsonPath|$.store.books[1:3,0,1]|] rootDoc `shouldBe` getVector books1To3And0And1Doc it "returns slice with queryQQ $[5:]" $ queryQQ [jsonPath|$[5:]|] alphaArr `shouldBe` getVector fgArr it "returns slice with queryQQ $[1:5:2]" $ queryQQ [jsonPath|$[1:5:2]|] alphaArr `shouldBe` getVector bdArr it "returns slice with queryQQ $[5:1:-2]" $ queryQQ [jsonPath|$[5:1:-2]|] alphaArr `shouldBe` getVector fdArr it "returns slice with queryQQ $[::-1]" $ queryQQ [jsonPath|$[::-1]|] alphaArr `shouldBe` getVector gfedcbaArr it "returns root with queryQQ $.*" $ queryQQ [jsonPath|$.*|] rootDoc `shouldBe` V.singleton storeDoc it "returns root with queryQQ $[*]" $ queryQQ [jsonPath|$[*]|] rootDoc `shouldBe` V.singleton storeDoc it "returns descendants with queryQQ $..*" $ queryQQ [jsonPath|$..*|] rfcExample1 `shouldBe` getVector rfcExample1Desc it "returns descendants with queryQQ $..[*]" $ queryQQ [jsonPath|$..[*]|] rfcExample1 `shouldBe` getVector rfcExample1Desc it "returns with filtering: number comparison" $ queryQQ [jsonPath|$.store.books[?@.price < 20]|] rootDoc `shouldBe` getVector lessThanPrice20Books it "returns with filtering: not operator" $ queryQQ [jsonPath|$.store.books[?!(@.price < 20)]|] rootDoc `shouldBe` getVector books0Doc it "returns with filtering: true value" $ queryQQ [jsonPath|$[?@.a == true]|] boolsAndNulls `shouldBe` getVector boolsAndNullsA it "returns with filtering: false value" $ queryQQ [jsonPath|$[?@.a == false]|] boolsAndNulls `shouldBe` getVector boolsAndNullsB it "returns with filtering: null value" $ queryQQ [jsonPath|$[?@.a == null]|] boolsAndNulls `shouldBe` getVector boolsAndNullsC it "returns with filtering: string comparison" $ queryQQ [jsonPath|$.store.books[?@.author == 'Jared Diamond']|] rootDoc `shouldBe` getVector books0Doc it "returns with filtering: test expression" $ queryQQ [jsonPath|$.store.books[?@.author]|] rootDoc `shouldBe` getVector booksDoc it "returns with filtering with spaced out segments: test expression" $ queryQQ [jsonPath|$ .store .books[?@ .author]|] rootDoc `shouldBe` getVector booksDoc it "returns with filtering: test expr gives empty with non-existent key" $ queryQQ [jsonPath|$.store.books[?@.not_here]|] rootDoc `shouldBe` V.empty