Repository: json-path/JsonPath
Branch: master
Commit: 62a4c9f0f65b
Files: 235
Total size: 1.0 MB
Directory structure:
gitextract_5g0slvqk/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── Procfile
├── README.md
├── build.gradle
├── changelog.md
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── json-path/
│ ├── build.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── jayway/
│ │ └── jsonpath/
│ │ ├── Configuration.java
│ │ ├── Criteria.java
│ │ ├── DocumentContext.java
│ │ ├── EvaluationListener.java
│ │ ├── Filter.java
│ │ ├── InvalidCriteriaException.java
│ │ ├── InvalidJsonException.java
│ │ ├── InvalidModificationException.java
│ │ ├── InvalidPathException.java
│ │ ├── JsonPath.java
│ │ ├── JsonPathException.java
│ │ ├── MapFunction.java
│ │ ├── Option.java
│ │ ├── ParseContext.java
│ │ ├── PathNotFoundException.java
│ │ ├── Predicate.java
│ │ ├── ReadContext.java
│ │ ├── TypeRef.java
│ │ ├── ValueCompareException.java
│ │ ├── WriteContext.java
│ │ ├── internal/
│ │ │ ├── CharacterIndex.java
│ │ │ ├── DefaultsImpl.java
│ │ │ ├── EvaluationAbortException.java
│ │ │ ├── EvaluationContext.java
│ │ │ ├── JsonContext.java
│ │ │ ├── JsonFormatter.java
│ │ │ ├── ParseContextImpl.java
│ │ │ ├── Path.java
│ │ │ ├── PathRef.java
│ │ │ ├── Utils.java
│ │ │ ├── filter/
│ │ │ │ ├── Evaluator.java
│ │ │ │ ├── EvaluatorFactory.java
│ │ │ │ ├── ExpressionNode.java
│ │ │ │ ├── FilterCompiler.java
│ │ │ │ ├── LogicalExpressionNode.java
│ │ │ │ ├── LogicalOperator.java
│ │ │ │ ├── PatternFlag.java
│ │ │ │ ├── RelationalExpressionNode.java
│ │ │ │ ├── RelationalOperator.java
│ │ │ │ ├── ValueNode.java
│ │ │ │ └── ValueNodes.java
│ │ │ ├── function/
│ │ │ │ ├── ParamType.java
│ │ │ │ ├── Parameter.java
│ │ │ │ ├── PassthruPathFunction.java
│ │ │ │ ├── PathFunction.java
│ │ │ │ ├── PathFunctionFactory.java
│ │ │ │ ├── json/
│ │ │ │ │ ├── Append.java
│ │ │ │ │ └── KeySetFunction.java
│ │ │ │ ├── latebinding/
│ │ │ │ │ ├── ILateBindingValue.java
│ │ │ │ │ ├── JsonLateBindingValue.java
│ │ │ │ │ └── PathLateBindingValue.java
│ │ │ │ ├── numeric/
│ │ │ │ │ ├── AbstractAggregation.java
│ │ │ │ │ ├── Average.java
│ │ │ │ │ ├── Max.java
│ │ │ │ │ ├── Min.java
│ │ │ │ │ ├── StandardDeviation.java
│ │ │ │ │ └── Sum.java
│ │ │ │ ├── sequence/
│ │ │ │ │ ├── AbstractSequenceAggregation.java
│ │ │ │ │ ├── First.java
│ │ │ │ │ ├── Index.java
│ │ │ │ │ └── Last.java
│ │ │ │ └── text/
│ │ │ │ ├── Concatenate.java
│ │ │ │ └── Length.java
│ │ │ └── path/
│ │ │ ├── ArrayIndexOperation.java
│ │ │ ├── ArrayIndexToken.java
│ │ │ ├── ArrayPathToken.java
│ │ │ ├── ArraySliceOperation.java
│ │ │ ├── ArraySliceToken.java
│ │ │ ├── CompiledPath.java
│ │ │ ├── EvaluationContextImpl.java
│ │ │ ├── FunctionPathToken.java
│ │ │ ├── PathCompiler.java
│ │ │ ├── PathToken.java
│ │ │ ├── PathTokenAppender.java
│ │ │ ├── PathTokenFactory.java
│ │ │ ├── PredicateContextImpl.java
│ │ │ ├── PredicatePathToken.java
│ │ │ ├── PropertyPathToken.java
│ │ │ ├── RootPathToken.java
│ │ │ ├── ScanPathToken.java
│ │ │ └── WildcardPathToken.java
│ │ └── spi/
│ │ ├── cache/
│ │ │ ├── Cache.java
│ │ │ ├── CacheProvider.java
│ │ │ ├── LRUCache.java
│ │ │ └── NOOPCache.java
│ │ ├── json/
│ │ │ ├── AbstractJsonProvider.java
│ │ │ ├── GsonJsonProvider.java
│ │ │ ├── Jackson3JsonNodeJsonProvider.java
│ │ │ ├── Jackson3JsonProvider.java
│ │ │ ├── JacksonJsonNodeJsonProvider.java
│ │ │ ├── JacksonJsonProvider.java
│ │ │ ├── JakartaJsonProvider.java
│ │ │ ├── JettisonProvider.java
│ │ │ ├── JsonOrgJsonProvider.java
│ │ │ ├── JsonProvider.java
│ │ │ ├── JsonSmartJsonProvider.java
│ │ │ └── TapestryJsonProvider.java
│ │ └── mapper/
│ │ ├── GsonMappingProvider.java
│ │ ├── Jackson3MappingProvider.java
│ │ ├── JacksonMappingProvider.java
│ │ ├── JakartaMappingProvider.java
│ │ ├── JsonOrgMappingProvider.java
│ │ ├── JsonSmartMappingProvider.java
│ │ ├── MappingException.java
│ │ ├── MappingProvider.java
│ │ └── TapestryMappingProvider.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── jayway/
│ │ └── jsonpath/
│ │ ├── BaseTest.java
│ │ ├── Configurations.java
│ │ ├── DeepScanTest.java
│ │ ├── EscapeTest.java
│ │ ├── EvaluationListenerTest.java
│ │ ├── FilterCompilerTest.java
│ │ ├── FilterParseTest.java
│ │ ├── FilterTest.java
│ │ ├── GsonJsonProviderTest.java
│ │ ├── InlineFilterTest.java
│ │ ├── Issue_487.java
│ │ ├── Issue_537.java
│ │ ├── Issue_721.java
│ │ ├── Issue_762.java
│ │ ├── Issue_786.java
│ │ ├── Issue_970.java
│ │ ├── Issue_973.java
│ │ ├── Jackson3JsonNodeJsonProviderMapperSupportTest.java
│ │ ├── Jackson3JsonNodeJsonProviderTest.java
│ │ ├── Jackson3Test.java
│ │ ├── JacksonJsonNodeJsonProviderMapperSupportTest.java
│ │ ├── JacksonJsonNodeJsonProviderTest.java
│ │ ├── JacksonTest.java
│ │ ├── JakartaJsonProviderTest.java
│ │ ├── JsonOrgJsonProviderTest.java
│ │ ├── JsonProviderTest.java
│ │ ├── JsonProviderTestObjectMapping.java
│ │ ├── MapperTest.java
│ │ ├── MultiPropTest.java
│ │ ├── OptionsTest.java
│ │ ├── PathCompilerTest.java
│ │ ├── PredicateTest.java
│ │ ├── ProviderInTest.java
│ │ ├── ReadContextTest.java
│ │ ├── ReturnTypeTest.java
│ │ ├── ScientificNotationTest.java
│ │ ├── TapestryJsonProviderTest.java
│ │ ├── TestSuppressExceptions.java
│ │ ├── TestUtils.java
│ │ ├── WriteTest.java
│ │ ├── internal/
│ │ │ ├── JsonContextTest.java
│ │ │ ├── UtilsTest.java
│ │ │ ├── filter/
│ │ │ │ ├── PatternFlagTest.java
│ │ │ │ ├── RegexpEvaluatorTest.java
│ │ │ │ └── RelationalOperatorTest.java
│ │ │ ├── function/
│ │ │ │ ├── BaseFunctionTest.java
│ │ │ │ ├── Issue191.java
│ │ │ │ ├── Issue234.java
│ │ │ │ ├── Issue273.java
│ │ │ │ ├── Issue612.java
│ │ │ │ ├── Issue629.java
│ │ │ │ ├── Issue680.java
│ │ │ │ ├── JSONEntityPathFunctionTest.java
│ │ │ │ ├── KeySetFunctionTest.java
│ │ │ │ ├── NestedFunctionTest.java
│ │ │ │ ├── NumericPathFunctionTest.java
│ │ │ │ └── SequentialPathFunctionTest.java
│ │ │ └── path/
│ │ │ └── PathTokenTest.java
│ │ ├── issue_613.java
│ │ └── old/
│ │ ├── ArraySlicingTest.java
│ │ ├── ComplianceTest.java
│ │ ├── FilterTest.java
│ │ ├── IssuesTest.java
│ │ ├── JsonPathTest.java
│ │ ├── JsonProviderTest.java
│ │ ├── NullHandlingTest.java
│ │ └── internal/
│ │ ├── ArrayIndexFilterTest.java
│ │ ├── ArrayPathTokenTest.java
│ │ ├── PredicatePathTokenTest.java
│ │ ├── PropertyPathTokenTest.java
│ │ ├── ScanPathTokenTest.java
│ │ ├── TestBase.java
│ │ └── TestInternal3.java
│ └── resources/
│ ├── issue_191.json
│ ├── issue_24.json
│ ├── issue_76.json
│ ├── issue_76_2.json
│ ├── json-test-doc.json
│ ├── json_array.json
│ ├── json_array_multiple_delete.json
│ ├── keyset.json
│ └── simplelogger.properties
├── json-path-assert/
│ ├── README.md
│ ├── build.gradle
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── com/
│ │ └── jayway/
│ │ ├── jsonassert/
│ │ │ ├── JsonAssert.java
│ │ │ ├── JsonAsserter.java
│ │ │ └── impl/
│ │ │ ├── JsonAsserterImpl.java
│ │ │ └── matcher/
│ │ │ ├── CollectionMatcher.java
│ │ │ ├── IsCollectionWithSize.java
│ │ │ ├── IsEmptyCollection.java
│ │ │ ├── IsMapContainingKey.java
│ │ │ ├── IsMapContainingValue.java
│ │ │ └── MapTypeSafeMatcher.java
│ │ └── jsonpath/
│ │ └── matchers/
│ │ ├── IsJson.java
│ │ ├── JsonPathMatchers.java
│ │ ├── WithJsonPath.java
│ │ └── WithoutJsonPath.java
│ └── test/
│ ├── java/
│ │ └── com/
│ │ └── jayway/
│ │ ├── jsonassert/
│ │ │ └── JsonAssertTest.java
│ │ └── jsonpath/
│ │ └── matchers/
│ │ ├── DemoTest.java
│ │ ├── HasNoJsonPathTest.java
│ │ ├── IsJsonFileTest.java
│ │ ├── IsJsonStringTest.java
│ │ ├── IsJsonTest.java
│ │ ├── JsonPathMatchersTest.java
│ │ ├── WithJsonPathTest.java
│ │ ├── WithoutJsonPathTest.java
│ │ └── helpers/
│ │ ├── ResourceHelpers.java
│ │ ├── StrictParsingConfiguration.java
│ │ └── TestingMatchers.java
│ └── resources/
│ ├── books.json
│ ├── example.json
│ ├── invalid.json
│ ├── links.json
│ └── lotto.json
├── settings.gradle
└── system.properties
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
---
name: Java CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 17, 21 ]
fail-fast: false
max-parallel: 4
name: JDK ${{ matrix.java }}
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: temurin
cache: 'gradle'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build --warning-mode all
- name: Run Tests
run: ./gradlew check
- name: Maven Install
run: ./gradlew clean publishToMavenLocal
...
================================================
FILE: .gitignore
================================================
.idea/
target
*.iws
*.ipr
*.iml
.classpath
.project
.settings
.springBeans
.DS_Store
.gradle
.java-version
TODO
gradle.properties
build
bin/
out/
================================================
FILE: .travis.yml
================================================
language: java
sudo: false
jdk:
- openjdk17
cache:
directories:
- $HOME/.gradle
arch:
- amd64
- ppc64le
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 Jayway
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Procfile
================================================
web: java $JAVA_OPTS -Dserver.http.port=$PORT -DresourceBase=json-path-web-test/build/resources/main/webapp/ -jar json-path-web-test/build/libs/json-path-web-test-*-all.jar
================================================
FILE: README.md
================================================
Jayway JsonPath
=====================
**A Java DSL for reading JSON documents.**
[](https://travis-ci.org/json-path/JsonPath)
[](https://maven-badges.herokuapp.com/maven-central/com.jayway.jsonpath/json-path)
[](http://www.javadoc.io/doc/com.jayway.jsonpath/json-path)
Jayway JsonPath is a Java port of [Stefan Goessner JsonPath implementation](http://goessner.net/articles/JsonPath/).
Getting Started
---------------
JsonPath is available at the Central Maven Repository. Maven users add this to your POM.
> [!NOTE]
> Version 3.0.0 Uses Java 17 baseline to support Jackson 3
```xml
com.jayway.jsonpath
json-path
3.0.0
```
If you need help ask questions at [Stack Overflow](http://stackoverflow.com/questions/tagged/jsonpath). Tag the
question 'jsonpath' and 'java'.
JsonPath expressions always refer to a JSON structure in the same way as XPath expression are used in combination
with an XML document. The "root member object" in JsonPath is always referred to as `$` regardless if it is an
object or array.
JsonPath expressions can use the dot–notation
`$.store.book[0].title`
or the bracket–notation
`$['store']['book'][0]['title']`
Operators
---------
| Operator | Description |
|:--------------------------|:----------------------------------------------------------------|
| `$` | The root element to query. This starts all path expressions. |
| `@` | The current node being processed by a filter predicate. |
| `*` | Wildcard. Available anywhere a name or numeric are required. |
| `..` | Deep scan. Available anywhere a name is required. |
| `.` | Dot-notated child |
| `['' (, '')]` | Bracket-notated child or children |
| `[ (, )]` | Array index or indexes |
| `[start:end]` | Array slice operator |
| `[?()]` | Filter expression. Expression must evaluate to a boolean value. |
Functions
---------
Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression.
The function output is dictated by the function itself.
| Function | Description | Output type |
|:------------|:-------------------------------------------------------------------------------------|:---------------------|
| `min()` | Provides the min value of an array of numbers | Double |
| `max()` | Provides the max value of an array of numbers | Double |
| `avg()` | Provides the average value of an array of numbers | Double |
| `stddev()` | Provides the standard deviation value of an array of numbers | Double |
| `length()` | Provides the length of an array | Integer |
| `sum()` | Provides the sum value of an array of numbers | Double |
| `keys()` | Provides the property keys (An alternative for terminal tilde `~`) | `Set` |
| `concat(X)` | Provides a concatinated version of the path output with a new item | like input |
| `append(X)` | add an item to the json path output array | like input |
| `first()` | Provides the first item of an array | Depends on the array |
| `last()` | Provides the last item of an array | Depends on the array |
| `index(X)` | Provides the item of an array of index: X, if the X is negative, take from backwards | Depends on the array |
Filter Operators
-----------------
Filters are logical expressions used to filter arrays. A typical filter would be `[?(@.age > 18)]` where `@` represents
the current item being processed. More complex filters can be created with logical operators `&&` and `||`. String
literals must be enclosed by single or double quotes (`[?(@.color == 'blue')]` or `[?(@.color == "blue")]`).
| Operator | Description |
|:-----------|:-------------------------------------------------------------------|
| `==` | left is equal to right (note that 1 is not equal to '1') |
| `!=` | left is not equal to right |
| `<` | left is less than right |
| `<=` | left is less or equal to right |
| `>` | left is greater than right |
| `>=` | left is greater than or equal to right |
| `=~` | left matches regular expression [?(@.name =~ /foo.*?/i)] |
| `in` | left exists in right [?(@.size in ['S', 'M'])] |
| `nin` | left does not exists in right |
| `subsetof` | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] |
| `anyof` | left has an intersection with right [?(@.sizes anyof ['M', 'L'])] |
| `noneof` | left has no intersection with right [?(@.sizes noneof ['M', 'L'])] |
| `size` | size of left (array or string) should match right |
| `empty` | left (array or string) should be empty |
Path Examples
-------------
Given the json
```javascript
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
```
| JsonPath | Result |
|:----------------------------------------|:-------------------------------------------------------------|
| `$.store.book[*].author` | The authors of all books |
| `$..author` | All authors |
| `$.store.*` | All things, both books and bicycles |
| `$.store..price` | The price of everything |
| `$..book[2]` | The third book |
| `$..book[-2]` | The second to last book |
| `$..book[0,1]` | The first two books |
| `$..book[:2]` | All books from index 0 (inclusive) until index 2 (exclusive) |
| `$..book[1:2]` | All books from index 1 (inclusive) until index 2 (exclusive) |
| `$..book[-2:]` | Last two books |
| `$..book[2:]` | All books from index 2 (inclusive) to last |
| `$..book[?(@.isbn)]` | All books with an ISBN number |
| `$.store.book[?(@.price < 10)]` | All books in store cheaper than 10 |
| `$..book[?(@.price <= $['expensive'])]` | All books in store that are not "expensive" |
| `$..book[?(@.author =~ /.*REES/i)]` | All books matching regex (ignore case) |
| `$..*` | Give me every thing
| `$..book.length()` | The number of books |
Reading a Document
------------------
The simplest most straight forward way to use JsonPath is via the static read API.
```java
String json = "...";
List authors = JsonPath.read(json, "$.store.book[*].author");
```
If you only want to read once this is OK. In case you need to read an other path as well this is not the way
to go since the document will be parsed every time you call JsonPath.read(...). To avoid the problem you can
parse the json first.
```java
String json = "...";
Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);
String author0 = JsonPath.read(document, "$.store.book[0].author");
String author1 = JsonPath.read(document, "$.store.book[1].author");
```
JsonPath also provides a fluent API. This is also the most flexible one.
```java
String json = "...";
ReadContext ctx = JsonPath.parse(json);
List authorsOfBooksWithISBN = ctx.read("$.store.book[?(@.isbn)].author");
List> expensiveBooks = JsonPath
.using(configuration)
.parse(json)
.read("$.store.book[?(@.price > 10)]", List.class);
```
What is Returned When?
----------------------
When using JsonPath in java its important to know what type you expect in your result. JsonPath will automatically
try to cast the result to the type expected by the invoker.
```java
//Will throw an java.lang.ClassCastException
List list = JsonPath.parse(json).read("$.store.book[0].author");
//Works fine
String author = JsonPath.parse(json).read("$.store.book[0].author");
```
When evaluating a path you need to understand the concept of when a path is `definite`. A path is `indefinite` if it
contains:
* `..` - a deep scan operator
* `?()` - an expression
* `[, (, )]` - multiple array indexes
`Indefinite` paths always returns a list (as represented by current JsonProvider).
By default a simple object mapper is provided by the MappingProvider SPI. This allows you to specify the return type you
want and the MappingProvider will
try to perform the mapping. In the example below mapping between `Long` and `Date` is demonstrated.
```java
String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
```
If you configure JsonPath to use `JacksonMappingProvider`, `Jackson3MappingProvider`, `GsonMappingProvider`,
or `JakartaJsonProvider` you can even
map your JsonPath output directly into POJO's.
```java
Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class);
```
To obtain full generics type information, use TypeRef.
```java
TypeRef> typeRef = new TypeRef>() {
};
List titles = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[*].title", typeRef);
```
Predicates
----------
There are three different ways to create filter predicates in JsonPath.
### Inline Predicates
Inline predicates are the ones defined in the path.
```java
List> books = JsonPath.parse(json)
.read("$.store.book[?(@.price < 10)]");
```
You can use `&&` and `||` to combine multiple predicates `[?(@.price < 10 && @.category == 'fiction')]` ,
`[?(@.category == 'reference' || @.price > 10)]`.
You can use `!` to negate a predicate `[?(!(@.price < 10 && @.category == 'fiction'))]`.
### Filter Predicates
Predicates can be built using the Filter API as shown below:
```java
import static com.jayway.jsonpath.JsonPath.parse;
import static com.jayway.jsonpath.Criteria.where;
import static com.jayway.jsonpath.Filter.filter;
...
...
Filter cheapFictionFilter = filter(
where("category").is("fiction").and("price").lte(10D)
);
List> books =
parse(json).read("$.store.book[?]", cheapFictionFilter);
```
Notice the placeholder `?` for the filter in the path. When multiple filters are provided they are applied in order
where the number of placeholders must match
the number of provided filters. You can specify multiple predicate placeholders in one filter operation `[?, ?]`, both
predicates must match.
Filters can also be combined with 'OR' and 'AND'
```java
Filter fooOrBar = filter(
where("foo").exists(true)).or(where("bar").exists(true)
);
Filter fooAndBar = filter(
where("foo").exists(true)).and(where("bar").exists(true)
);
```
### Roll Your Own
Third option is to implement your own predicates
```java
Predicate booksWithISBN = new Predicate() {
@Override
public boolean apply(PredicateContext ctx) {
return ctx.item(Map.class).containsKey("isbn");
}
};
List> books =
reader.read("$.store.book[?].isbn", List.class, booksWithISBN);
```
Path vs Value
-------------
In the Goessner implementation a JsonPath can return either `Path` or `Value`. `Value` is the default and what all the
examples above are returning. If you rather have the path of the elements our query is hitting this can be achieved with
an option.
```java
Configuration conf = Configuration.builder()
.options(Option.AS_PATH_LIST).build();
List pathList = using(conf).parse(json).read("$..author");
assertThat(pathList).
containsExactly(
"$['store']['book'][0]['author']",
"$['store']['book'][1]['author']",
"$['store']['book'][2]['author']",
"$['store']['book'][3]['author']");
```
Set a value
-----------
The library offers the possibility to set a value.
```java
String newJson = JsonPath.parse(json).set("$['store']['book'][0]['author']", "Paul").jsonString();
```
Tweaking Configuration
----------------------
### Options
When creating your Configuration there are a few option flags that can alter the default behaviour.
**DEFAULT_PATH_LEAF_TO_NULL**
This option makes JsonPath return null for missing leafs. Consider the following json
```javascript
[
{
"name" : "john",
"gender" : "male"
},
{
"name" : "ben"
}
]
```
```java
Configuration conf = Configuration.defaultConfiguration();
//Works fine
String gender0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
//PathNotFoundException thrown
String gender1 = JsonPath.using(conf).parse(json).read("$[1]['gender']");
Configuration conf2 = conf.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
//Works fine
String gender0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
//Works fine (null is returned)
String gender1 = JsonPath.using(conf2).parse(json).read("$[1]['gender']");
```
**ALWAYS_RETURN_LIST**
This option configures JsonPath to return a list even when the path is `definite`.
```java
Configuration conf = Configuration.defaultConfiguration();
//ClassCastException thrown
List genders0 = JsonPath.using(conf).parse(json).read("$[0]['gender']");
Configuration conf2 = conf.addOptions(Option.ALWAYS_RETURN_LIST);
//Works fine
List genders0 = JsonPath.using(conf2).parse(json).read("$[0]['gender']");
```
**SUPPRESS_EXCEPTIONS**
This option makes sure no exceptions are propagated from path evaluation. It follows these simple rules:
* If option `ALWAYS_RETURN_LIST` is present an empty list will be returned
* If option `ALWAYS_RETURN_LIST` is **NOT** present null returned
**REQUIRE_PROPERTIES**
This option configures JsonPath to require properties defined in path when an `indefinite` path is evaluated.
```java
Configuration conf = Configuration.defaultConfiguration();
//Works fine
List genders = JsonPath.using(conf).parse(json).read("$[*]['gender']");
Configuration conf2 = conf.addOptions(Option.REQUIRE_PROPERTIES);
//PathNotFoundException thrown
List genders = JsonPath.using(conf2).parse(json).read("$[*]['gender']");
```
### JsonProvider SPI
JsonPath is shipped with five different JsonProviders:
* [JsonSmartJsonProvider](https://github.com/netplex/json-smart-v2) (default)
* [JacksonJsonProvider](https://github.com/FasterXML/jackson)
* [JacksonJsonNodeJsonProvider](https://github.com/FasterXML/jackson)
* [JacksonJson3Provider](https://github.com/FasterXML/jackson)
* [JacksonJson3NodeJsonProvider](https://github.com/FasterXML/jackson)
* [GsonJsonProvider](https://code.google.com/p/google-gson/)
* [JsonOrgJsonProvider](https://github.com/stleary/JSON-java)
* [JakartaJsonProvider](https://javaee.github.io/jsonp/)
Changing the configuration defaults as demonstrated should only be done when your application is being initialized.
Changes during runtime is strongly discouraged, especially in multi threaded applications.
```java
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
@Override
public JsonProvider jsonProvider () {
return jsonProvider;
}
@Override
public MappingProvider mappingProvider () {
return mappingProvider;
}
@Override
public Set options () {
return EnumSet.noneOf(Option.class);
}
});
```
Note that the JacksonJsonProvider requires `com.fasterxml.jackson.core:jackson-databind:2.20.1`, the
Jackson3JsonProvider requires `tools.jackson.core:jackson-databind:3.0.3` and the GsonJsonProvider
requires `com.google.code.gson:gson:2.3.1` on your classpath.
Both of Jakarta EE 9 [JSON-P (JSR-342)](https://javaee.github.io/jsonp/) and [JSON-B (JSR-367)](http://json-b.net/)
providers expect at least Java 8 and require compatible JSON API implementations (such
as [Eclipse Glassfish](https://projects.eclipse.org/projects/ee4j.jsonp)
and [Eclipse Yasson](https://projects.eclipse.org/projects/ee4j.yasson)) on application runtime classpath; such
implementations may also be provided by Java EE application container. Please also note that Apache Johnzon is not
classpath-compatible with Jakarta EE 9 specification yet, and if JSON-B mapping provider is chosen then JSON-P provider
must be configured and used, too.
One peculiarity of Jakarta EE 9 specifications for JSON processing and databinding (mapping) is immutability of Json
arrays and objects as soon as they are fully parsed or written to. To respect the API specification, but allow JsonPath
to modify Json documents through add, set/put, replace, and delete operations, `JakartaJsonProvider` has to be
initiliazed with optional `true` argument:
* `JsonProvider jsonProvider = new JakartaJsonProvider(true)` (enable mutable Json arrays and objects)
* `JsonProvider jsonProvider = new JakartaJsonProvider()` (default, strict JSON-P API compliance)
All lookup and read operations with JsonPath are supported regardless of initilization mode. Default mode also needs
less memory and is more performant.
### Cache SPI
In JsonPath 2.1.0 a new Cache SPI was introduced. This allows API consumers to configure path caching in a way that
suits their needs. The cache must be configured before it is accesses for the first time or a JsonPathException is
thrown. JsonPath ships with two cache implementations
* `com.jayway.jsonpath.spi.cache.LRUCache` (default, thread safe)
* `com.jayway.jsonpath.spi.cache.NOOPCache` (no cache)
If you want to implement your own cache the API is simple.
```java
CacheProvider.setCache(new Cache() {
//Not thread safe simple cache
private Map map = new HashMap();
@Override
public JsonPath get (String key){
return map.get(key);
}
@Override
public void put (String key, JsonPath jsonPath){
map.put(key, jsonPath);
}
});
```
[](https://github.com/igrigorik/ga-beacon)
================================================
FILE: build.gradle
================================================
buildscript {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:6.1.0'
}
}
ext {
libs = [
jsonSmart : 'net.minidev:json-smart:2.6.0',
slf4jApi : 'org.slf4j:slf4j-api:2.0.17',
gson : 'com.google.code.gson:gson:2.13.2',
hamcrest : 'org.hamcrest:hamcrest:3.0',
jacksonDatabind : 'com.fasterxml.jackson.core:jackson-databind:2.20.1',
jacksonDatabind3: 'tools.jackson.core:jackson-databind:3.0.4',
jettison : 'org.codehaus.jettison:jettison:1.5.4',
jsonOrg : 'org.json:json:20250517',
tapestryJson : 'org.apache.tapestry:tapestry-json:5.9.0',
jakartaJsonP : 'jakarta.json:jakarta.json-api:2.1.3',
jakartaJsonB : 'jakarta.json.bind:jakarta.json.bind-api:2.0.0',
test : [
'commons-io:commons-io:2.20.0',
'org.junit.jupiter:junit-jupiter:5.10.1',
'org.assertj:assertj-core:3.25.1',
'org.hamcrest:hamcrest:3.0',
'org.glassfish:jakarta.json:2.0.1',
'org.eclipse:yasson:2.0.4',
'org.slf4j:slf4j-simple:2.0.17',
'com.google.code.gson:gson:2.13.2',
'org.hamcrest:hamcrest:3.0',
'com.fasterxml.jackson.core:jackson-databind:2.20.1',
'tools.jackson.core:jackson-databind:3.0.3',
'org.codehaus.jettison:jettison:1.5.4',
'org.json:json:20250517',
'org.apache.tapestry:tapestry-json:5.9.0',
'jakarta.json:jakarta.json-api:2.1.3',
'jakarta.json.bind:jakarta.json.bind-api:2.0.0'
]
]
snapshotVersion = false
}
allprojects {
ext.displayName = null
ext.buildTimestamp = new Date().format('yyyy-MM-dd HH:mm:ss')
group = 'com.jayway.jsonpath'
version = '3.0.0' + (snapshotVersion ? "-SNAPSHOT" : "")
}
subprojects {
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'maven-publish'
apply plugin: 'signing'
apply plugin: 'biz.aQute.bnd.builder'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
java {
withJavadocJar()
withSourcesJar()
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
signing {
sign publishing.publications
required { !snapshotVersion && gradle.taskGraph.hasTask("publish") }
}
javadoc {
if (JavaVersion.current().isJava8Compatible()) {
options.addStringOption('Xdoclint:none', '-quiet')
}
if (JavaVersion.current().isJava9Compatible()) {
options.addBooleanOption('html5', true)
}
}
publishing {
repositories {
maven {
name = 'ossrh-staging-api'
url = 'https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/'
credentials {
username = project.hasProperty('sonatypeOssUsername') ? sonatypeOssUsername : "Unknown user"
password = project.hasProperty('sonatypeOssPassword') ? sonatypeOssPassword : "Unknown password"
}
}
}
publications {
mavenJava(MavenPublication) {
from components.java
artifactId = project.name
//artifactId = jar.archiveBaseName
pom {
name = project.name
description = 'A library to query and verify JSON'
url = 'https://github.com/jayway/JsonPath'
licenses {
license {
name = "The Apache Software License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
distribution = "repo"
}
}
scm {
url = 'scm:git:git://github.com/jayway/JsonPath.git'
connection = 'scm:git:git://github.com/jayway/JsonPath.git'
developerConnection = 'scm:git:git://github.com/jayway/JsonPath.git'
}
developers {
developer {
id = 'kalle.stenflo'
name = 'Kalle Stenflo'
email = 'kalle.stenflo (a) gmail.com'
}
}
}
}
}
}
}
wrapper {
gradleVersion = '8.5'
}
================================================
FILE: changelog.md
================================================
2.2.0 (2016-02-29)
===========
* Upgraded dependency versions
* Hamcrest-matcher support in json-path-assert
* Bug fixes and improvements
2.1.0 (2015-11-22)
===========
* Upgraded dependency versions
* Introduced Cache SPI
* Introduced path functions
* Introduced JsonOrgJsonProvider
* OSGI improvements
* Inline filters supports same operators as Filter builders
* Improved filter evaluation
* Internal makeovers
* New write operations : map(...) and renameKey(...)
2.0.0 (2015-03-19)
=================
* Upgraded dependency versions
* Moved JsonProvider and MappingProvider implementations out of the interal package **OSGi**
* Removed HTTP provider and methods
* Add an unwrap(Object) method to JsonProvider, use it when extracting values for Criteria evaluation **breaks JsonProvider SPI**
* Fixed issue #71 - esacpe character in inline predicates
`JsonPath.read(json, "$.logs[?(@.message == 'it\\'s here')].message");`
* New method `jsonString()` added to `ReadContext` and `WriteContext` to extract json model as a string
* Path does not have to be definite in filter API `filter(where("authors[*].lastName").contains("Waugh"))`
1.2.0 (2014-11-11)
==================
* Added EvaluationListener interface that allows abortion of evaluation if criteria is fulfilled.
this makes it possible to limit the number of results to fetch when a document is scanned. Also
added utility method to limit results `JsonPath.parse(json).limit(1).read("$..title", List.class);`
* Added support for OR in inline filters `[?(@.foo == 'bar' || @.foo == 'baz')]`
* Upgrade json-smart to 2.1.0
* Support for Update and Delete by path. **breaks JsonProvider SPI**
`parse(JSON_DOCUMENT).set("$.store.book[*].display-price", 1)`
`parse(JSON_DOCUMENT).put("$.store.book[1]", "new-key", "new-val")`
`parse(JSON_DOCUMENT).add("$.store.book", newBook)`
`parse(JSON_DOCUMENT).delete("$.store.book[1].display-price")`
* Support regex in inline filters (ruby syntax)
`parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /reference/)].author")`
`parse(JSON_DOCUMENT).read("$.store.book[?(@.category =~ /REFERENCE/i)].author")`
* Inline filter does not require path statement on left side of operator
`parse(JSON_DOCUMENT).read("$.store.book[?(@.category == 'reference')].author")`
`parse(JSON_DOCUMENT).read("$.store.book[?('reference' == @.category)].author")`
* Negate exist checks in inline filters (not defined or null)
`parse(JSON_DOCUMENT).read("$.store.book[?(!@.isbn)]")`
* Improved object mapping with Jackson and Gson (now handles generic types)
* JacksonJsonNodeJsonProvider supporting path operations on `com.fasterxml.jackson.databind.JsonNode`
* Exceptions thrown by JsonPath.compile are now wrapped in an InvalidPathException
* Fixed Deep scanning issue (#60)
1.1.0 (2014-10-01)
==================
* Reintroduced method JsonProvider.createMap(). This should never have been removed. **NOTE: This is a breaking change if you implemented your own JsonProvider based on the 1.0.0 API**
* Filters threw exception if an item being filtered did not contain the path being filtered upon.
* Multi-property selects works as it did in 0.9. e.g. `$[*]['category', 'price']`
* Cache results when predicates refer to path in document e.g. `$[*][?(@.price <= $.max-price)]` will only evaluate `$.max-price` once.
1.0.0 (2014-09-26)
==================
* Complete rewrite of internals. Major API changes.
* Better compliance with the Goessner implementation
Release history
===============
* 0.9.0 (2013-09-26)
* 0.8.1 (2012-04-16)
* 0.8.0 (2012-03-08)
* 0.5.6 (2012-02-09)
* 0.5.5 (2011-07-15)
* 0.5.4 (2011-06-26)
* 0.5.3 (2011-02-18)
* 0.5.2 (2011-02-08)
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradlew
================================================
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: json-path/build.gradle
================================================
description = "Java port of Stefan Goessner JsonPath."
jar {
bnd(
'Automatic-Module-Name': 'json.path',
'Implementation-Title': 'json-path', 'Implementation-Version': archiveVersion,
'Import-Package': 'org.json.*;resolution:=optional, com.google.gson.*;resolution:=optional, com.fasterxml.jackson.*;resolution:=optional, org.apache.tapestry5.json.*;resolution:=optional, org.codehaus.jettison.*;resolution:=optional, jakarta.json.*;resolution:=optional, *',
'Export-Package': 'com.jayway.jsonpath,com.jayway.jsonpath.spi,com.jayway.jsonpath.spi.cache,com.jayway.jsonpath.spi.json,com.jayway.jsonpath.spi.mapper'
)
}
dependencies {
implementation libs.jsonSmart
implementation libs.slf4jApi
compileOnly libs.jacksonDatabind // , optional
compileOnly libs.jacksonDatabind3 // , optional
compileOnly libs.gson// , optional
compileOnly libs.jsonOrg// , optional
compileOnly libs.tapestryJson// , optional
compileOnly libs.jettison// , optional
compileOnly libs.jakartaJsonP// , optional
compileOnly libs.jakartaJsonB// , optional
testImplementation libs.test
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/Configuration.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.DefaultsImpl;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import java.util.*;
import static com.jayway.jsonpath.internal.Utils.notNull;
import static java.util.Arrays.asList;
/**
* Immutable configuration object
*/
public class Configuration {
private static Defaults DEFAULTS = null;
/**
* Set Default configuration
* @param defaults default configuration settings
*/
public static synchronized void setDefaults(Defaults defaults){
DEFAULTS = defaults;
}
private static Defaults getEffectiveDefaults(){
if (DEFAULTS == null) {
return DefaultsImpl.INSTANCE;
} else {
return DEFAULTS;
}
}
private final JsonProvider jsonProvider;
private final MappingProvider mappingProvider;
private final Set options;
private final Collection evaluationListeners;
private Configuration(JsonProvider jsonProvider, MappingProvider mappingProvider, EnumSet options, Collection evaluationListeners) {
notNull(jsonProvider, "jsonProvider can not be null");
notNull(mappingProvider, "mappingProvider can not be null");
notNull(options, "setOptions can not be null");
notNull(evaluationListeners, "evaluationListeners can not be null");
this.jsonProvider = jsonProvider;
this.mappingProvider = mappingProvider;
this.options = Collections.unmodifiableSet(options);
this.evaluationListeners = Collections.unmodifiableCollection(evaluationListeners);
}
/**
* Creates a new Configuration by the provided evaluation listeners to the current listeners
* @param evaluationListener listeners
* @return a new configuration
*/
public Configuration addEvaluationListeners(EvaluationListener... evaluationListener){
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListener).build();
}
/**
* Creates a new Configuration with the provided evaluation listeners
* @param evaluationListener listeners
* @return a new configuration
*/
public Configuration setEvaluationListeners(EvaluationListener... evaluationListener){
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListener).build();
}
/**
* Returns the evaluation listeners registered in this configuration
* @return the evaluation listeners
*/
public Collection getEvaluationListeners(){
return evaluationListeners;
}
/**
* Creates a new Configuration based on the given {@link com.jayway.jsonpath.spi.json.JsonProvider}
* @param newJsonProvider json provider to use in new configuration
* @return a new configuration
*/
public Configuration jsonProvider(JsonProvider newJsonProvider) {
return Configuration.builder().jsonProvider(newJsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
}
/**
* Returns {@link com.jayway.jsonpath.spi.json.JsonProvider} used by this configuration
* @return jsonProvider used
*/
public JsonProvider jsonProvider() {
return jsonProvider;
}
/**
* Creates a new Configuration based on the given {@link com.jayway.jsonpath.spi.mapper.MappingProvider}
* @param newMappingProvider mapping provider to use in new configuration
* @return a new configuration
*/
public Configuration mappingProvider(MappingProvider newMappingProvider) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(newMappingProvider).options(options).evaluationListener(evaluationListeners).build();
}
/**
* Returns {@link com.jayway.jsonpath.spi.mapper.MappingProvider} used by this configuration
* @return mappingProvider used
*/
public MappingProvider mappingProvider() {
return mappingProvider;
}
/**
* Creates a new configuration by adding the new options to the options used in this configuration.
* @param options options to add
* @return a new configuration
*/
public Configuration addOptions(Option... options) {
EnumSet opts = EnumSet.noneOf(Option.class);
opts.addAll(this.options);
opts.addAll(asList(options));
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(opts).evaluationListener(evaluationListeners).build();
}
/**
* Creates a new configuration with the provided options. Options in this configuration are discarded.
* @param options
* @return the new configuration instance
*/
public Configuration setOptions(Option... options) {
return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build();
}
/**
* Returns the options used by this configuration
* @return the new configuration instance
*/
public Set getOptions() {
return options;
}
/**
* Check if this configuration contains the given option
* @param option option to check
* @return true if configurations contains option
*/
public boolean containsOption(Option option){
return options.contains(option);
}
/**
* Creates a new configuration based on default values
* @return a new configuration based on defaults
*/
public static Configuration defaultConfiguration() {
Defaults defaults = getEffectiveDefaults();
return Configuration.builder().jsonProvider(defaults.jsonProvider()).options(defaults.options()).build();
}
/**
* Returns a new ConfigurationBuilder
* @return a builder
*/
public static ConfigurationBuilder builder() {
return new ConfigurationBuilder();
}
/**
* Configuration builder
*/
public static class ConfigurationBuilder {
private JsonProvider jsonProvider;
private MappingProvider mappingProvider;
private EnumSet options = EnumSet.noneOf(Option.class);
private Collection evaluationListener = new ArrayList();
public ConfigurationBuilder jsonProvider(JsonProvider provider) {
this.jsonProvider = provider;
return this;
}
public ConfigurationBuilder mappingProvider(MappingProvider provider) {
this.mappingProvider = provider;
return this;
}
public ConfigurationBuilder options(Option... flags) {
if(flags.length > 0) {
this.options.addAll(asList(flags));
}
return this;
}
public ConfigurationBuilder options(Set options) {
this.options.addAll(options);
return this;
}
public ConfigurationBuilder evaluationListener(EvaluationListener... listener){
this.evaluationListener = Arrays.asList(listener);
return this;
}
public ConfigurationBuilder evaluationListener(Collection listeners){
this.evaluationListener = listeners == null ? Collections.emptyList() : listeners;
return this;
}
public Configuration build() {
if (jsonProvider == null || mappingProvider == null) {
final Defaults defaults = getEffectiveDefaults();
if (jsonProvider == null) {
jsonProvider = defaults.jsonProvider();
}
if (mappingProvider == null){
mappingProvider = defaults.mappingProvider();
}
}
return new Configuration(jsonProvider, mappingProvider, options, evaluationListener);
}
}
public interface Defaults {
/**
* Returns the default {@link com.jayway.jsonpath.spi.json.JsonProvider}
* @return default json provider
*/
JsonProvider jsonProvider();
/**
* Returns default setOptions
* @return setOptions
*/
Set options();
/**
* Returns the default {@link com.jayway.jsonpath.spi.mapper.MappingProvider}
*
* @return default mapping provider
*/
MappingProvider mappingProvider();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Configuration that = (Configuration) o;
return jsonProvider.getClass() == that.jsonProvider.getClass() &&
mappingProvider.getClass() == that.mappingProvider.getClass() &&
Objects.equals(options, that.options);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/Criteria.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.filter.RelationalExpressionNode;
import com.jayway.jsonpath.internal.filter.RelationalOperator;
import com.jayway.jsonpath.internal.filter.ValueNode;
import com.jayway.jsonpath.internal.filter.ValueNodes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.Utils.notNull;
import static com.jayway.jsonpath.internal.filter.ValueNodes.PredicateNode;
import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
/**
*
*/
@SuppressWarnings("unchecked")
public class Criteria implements Predicate {
private final List criteriaChain;
private ValueNode left;
private RelationalOperator criteriaType;
private ValueNode right;
private Criteria(List criteriaChain, ValueNode left) {
this.left = left;
this.criteriaChain = criteriaChain;
this.criteriaChain.add(this);
}
private Criteria(ValueNode left) {
this(new LinkedList(), left);
}
@Override
public boolean apply(PredicateContext ctx) {
for (RelationalExpressionNode expressionNode : toRelationalExpressionNodes()) {
if(!expressionNode.apply(ctx)){
return false;
}
}
return true;
}
@Override
public String toString() {
return Utils.join(" && ", toRelationalExpressionNodes());
}
private Collection toRelationalExpressionNodes(){
List nodes = new ArrayList(criteriaChain.size());
for (Criteria criteria : criteriaChain) {
nodes.add(new RelationalExpressionNode(criteria.left, criteria.criteriaType, criteria.right));
}
return nodes;
}
/**
* Static factory method to create a Criteria using the provided key
*
* @param key filed name
* @return the new criteria
*/
@Deprecated
//This should be private.It exposes internal classes
public static Criteria where(Path key) {
return new Criteria(ValueNode.createPathNode(key));
}
/**
* Static factory method to create a Criteria using the provided key
*
* @param key filed name
* @return the new criteria
*/
public static Criteria where(String key) {
return new Criteria(ValueNode.toValueNode(prefixPath(key)));
}
/**
* Static factory method to create a Criteria using the provided key
*
* @param key ads new filed to criteria
* @return the criteria builder
*/
public Criteria and(String key) {
checkComplete();
return new Criteria(this.criteriaChain, ValueNode.toValueNode(prefixPath(key)));
}
/**
* Creates a criterion using equality
*
* @param o
* @return the criteria
*/
public Criteria is(Object o) {
this.criteriaType = RelationalOperator.EQ;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* Creates a criterion using equality
*
* @param o
* @return the criteria
*/
public Criteria eq(Object o) {
return is(o);
}
/**
* Creates a criterion using the != operator
*
* @param o
* @return the criteria
*/
public Criteria ne(Object o) {
this.criteriaType = RelationalOperator.NE;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* Creates a criterion using the < operator
*
* @param o
* @return the criteria
*/
public Criteria lt(Object o) {
this.criteriaType = RelationalOperator.LT;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* Creates a criterion using the <= operator
*
* @param o
* @return the criteria
*/
public Criteria lte(Object o) {
this.criteriaType = RelationalOperator.LTE;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* Creates a criterion using the > operator
*
* @param o
* @return the criteria
*/
public Criteria gt(Object o) {
this.criteriaType = RelationalOperator.GT;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* Creates a criterion using the >= operator
*
* @param o
* @return the criteria
*/
public Criteria gte(Object o) {
this.criteriaType = RelationalOperator.GTE;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* Creates a criterion using a Regex
*
* @param pattern
* @return the criteria
*/
public Criteria regex(Pattern pattern) {
notNull(pattern, "pattern can not be null");
this.criteriaType = RelationalOperator.REGEX;
this.right = ValueNode.toValueNode(pattern);
return this;
}
/**
* The in operator is analogous to the SQL IN modifier, allowing you
* to specify an array of possible matches.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria in(Object... o) {
return in(Arrays.asList(o));
}
/**
* The in operator is analogous to the SQL IN modifier, allowing you
* to specify an array of possible matches.
*
* @param c the collection containing the values to match against
* @return the criteria
*/
public Criteria in(Collection> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.IN;
this.right = new ValueListNode(c);
return this;
}
/**
* The contains operator asserts that the provided object is contained
* in the result. The object that should contain the input can be either an object or a String.
*
* @param o that should exists in given collection or
* @return the criteria
*/
public Criteria contains(Object o) {
this.criteriaType = RelationalOperator.CONTAINS;
this.right = ValueNode.toValueNode(o);
return this;
}
/**
* The nin operator is similar to $in except that it selects objects for
* which the specified field does not have any value in the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria nin(Object... o) {
return nin(Arrays.asList(o));
}
/**
* The nin operator is similar to $in except that it selects objects for
* which the specified field does not have any value in the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria nin(Collection> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.NIN;
this.right = new ValueListNode(c);
return this;
}
/**
* The subsetof operator selects objects for which the specified field is
* an array whose elements comprise a subset of the set comprised by the elements of
* the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria subsetof(Object... o) {
return subsetof(Arrays.asList(o));
}
/**
* The subsetof operator selects objects for which the specified field is
* an array whose elements comprise a subset of the set comprised by the elements of
* the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria subsetof(Collection> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.SUBSETOF;
this.right = new ValueListNode(c);
return this;
}
/**
* The anyof operator selects objects for which the specified field is
* an array that contain at least an element in the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria anyof(Object... o) {
return subsetof(Arrays.asList(o));
}
/**
* The anyof operator selects objects for which the specified field is
* an array that contain at least an element in the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria anyof(Collection> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.ANYOF;
this.right = new ValueListNode(c);
return this;
}
/**
* The noneof operator selects objects for which the specified field is
* an array that does not contain any of the elements of the specified array.
*
* @param o the values to match against
* @return the criteria
*/
public Criteria noneof(Object... o) {
return noneof(Arrays.asList(o));
}
/**
* The noneof operator selects objects for which the specified field is
* an array that does not contain any of the elements of the specified array.
*
* @param c the values to match against
* @return the criteria
*/
public Criteria noneof(Collection> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.NONEOF;
this.right = new ValueListNode(c);
return this;
}
/**
* The all operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
*
* @param o
* @return the criteria
*/
public Criteria all(Object... o) {
return all(Arrays.asList(o));
}
/**
* The all operator is similar to $in, but instead of matching any value
* in the specified array all values in the array must be matched.
*
* @param c
* @return the criteria
*/
public Criteria all(Collection> c) {
notNull(c, "collection can not be null");
this.criteriaType = RelationalOperator.ALL;
this.right = new ValueListNode(c);
return this;
}
/**
* The size operator matches:
*
*
* array with the specified number of elements.
* string with given length.
*
*
* @param size
* @return the criteria
*/
public Criteria size(int size) {
this.criteriaType = RelationalOperator.SIZE;
this.right = ValueNode.toValueNode(size);
return this;
}
/**
* The $type operator matches values based on their Java JSON type.
*
* Supported types are:
*
* List.class
* Map.class
* String.class
* Number.class
* Boolean.class
*
* Other types evaluates to false
*
* @param clazz
* @return the criteria
*/
public Criteria type(Class> clazz) {
this.criteriaType = RelationalOperator.TYPE;
this.right = ValueNode.createClassNode(clazz);
return this;
}
/**
* Check for existence (or lack thereof) of a field.
*
* @param shouldExist
* @return the criteria
*/
public Criteria exists(boolean shouldExist) {
this.criteriaType = RelationalOperator.EXISTS;
this.right = ValueNode.toValueNode(shouldExist);
this.left = left.asPathNode().asExistsCheck(shouldExist);
return this;
}
/**
* The notEmpty operator checks that an array or String is not empty.
*
* @return the criteria
*/
@Deprecated
public Criteria notEmpty() {
return empty(false);
}
/**
* The notEmpty operator checks that an array or String is empty.
*
* @param empty should be empty
* @return the criteria
*/
public Criteria empty(boolean empty) {
this.criteriaType = RelationalOperator.EMPTY;
this.right = empty ? ValueNodes.TRUE : ValueNodes.FALSE;
return this;
}
/**
* The matches operator checks that an object matches the given predicate.
*
* @param p
* @return the criteria
*/
public Criteria matches(Predicate p) {
this.criteriaType = RelationalOperator.MATCHES;
this.right = new PredicateNode(p);
return this;
}
/**
* Parse the provided criteria
*
* Deprecated use {@link Filter#parse(String)}
*
* @param criteria
* @return a criteria
*/
@Deprecated
public static Criteria parse(String criteria) {
if(criteria == null){
throw new InvalidPathException("Criteria can not be null");
}
String[] split = criteria.trim().split(" ");
if(split.length == 3){
return create(split[0], split[1], split[2]);
} else if(split.length == 1){
return create(split[0], "EXISTS", "true");
} else {
throw new InvalidPathException("Could not parse criteria");
}
}
/**
* Creates a new criteria
* @param left path to evaluate in criteria
* @param operator operator
* @param right expected value
* @return a new Criteria
*/
@Deprecated
public static Criteria create(String left, String operator, String right) {
Criteria criteria = new Criteria(ValueNode.toValueNode(left));
criteria.criteriaType = RelationalOperator.fromString(operator);
criteria.right = ValueNode.toValueNode(right);
return criteria;
}
private static String prefixPath(String key){
if (!key.startsWith("$") && !key.startsWith("@")) {
key = "@." + key;
}
return key;
}
private void checkComplete(){
boolean complete = (left != null && criteriaType != null && right != null);
if(!complete){
throw new JsonPathException("Criteria build exception. Complete on criteria before defining next.");
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/DocumentContext.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public interface DocumentContext extends ReadContext, WriteContext {
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/EvaluationListener.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
/**
* A listener that can be registered on a {@link com.jayway.jsonpath.Configuration} that is notified when a
* result is added to the result of this path evaluation.
*/
public interface EvaluationListener {
/**
* Callback invoked when result is found
* @param found the found result
* @return continuation instruction
*/
EvaluationContinuation resultFound(FoundResult found);
enum EvaluationContinuation {
/**
* Evaluation continues
*/
CONTINUE,
/**
* Current result is included but no further evaluation will be performed.
*/
ABORT
}
/**
*
*/
interface FoundResult {
/**
* the index of this result. First result i 0
* @return index
*/
int index();
/**
* The path of this result
* @return path
*/
String path();
/**
* The result object
* @return the result object
*/
Object result();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/Filter.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.filter.FilterCompiler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import static java.util.Arrays.asList;
/**
*
*/
public abstract class Filter implements Predicate {
/**
* Creates a new Filter based on given criteria
* @param predicate criteria
* @return a new Filter
*/
public static Filter filter(Predicate predicate) {
return new SingleFilter(predicate);
}
/**
* Create a new Filter based on given list of criteria.
* @param predicates list of criteria all needs to evaluate to true
* @return the filter
*/
public static Filter filter(Collection predicates) {
return new AndFilter(predicates);
}
@Override
public abstract boolean apply(PredicateContext ctx);
public Filter or(final Predicate other){
return new OrFilter(this, other);
}
public Filter and(final Predicate other){
return new AndFilter(this, other);
}
/**
* Parses a filter. The filter must match [?()] , white spaces are ignored.
* @param filter filter string to parse
* @return the filter
*/
public static Filter parse(String filter){
return FilterCompiler.compile(filter);
}
private static final class SingleFilter extends Filter {
private final Predicate predicate;
private SingleFilter(Predicate predicate) {
this.predicate = predicate;
}
@Override
public boolean apply(PredicateContext ctx) {
return predicate.apply(ctx);
}
@Override
public String toString() {
String predicateString = predicate.toString();
if(predicateString.startsWith("(")){
return "[?" + predicateString + "]";
} else {
return "[?(" + predicateString + ")]";
}
}
}
private static final class AndFilter extends Filter {
private final Collection predicates;
private AndFilter(Collection predicates) {
this.predicates = predicates;
}
private AndFilter(Predicate left, Predicate right) {
this(asList(left, right));
}
public Filter and(final Predicate other){
Collection newPredicates = new ArrayList(predicates);
newPredicates.add(other);
return new AndFilter(newPredicates);
}
@Override
public boolean apply(PredicateContext ctx) {
for (Predicate predicate : predicates) {
if(!predicate.apply(ctx)){
return false;
}
}
return true;
}
@Override
public String toString() {
Iterator i = predicates.iterator();
StringBuilder sb = new StringBuilder();
sb.append("[?(");
while (i.hasNext()){
String p = i.next().toString();
if(p.startsWith("[?(")){
p = p.substring(3, p.length() - 2);
}
sb.append(p);
if(i.hasNext()){
sb.append(" && ");
}
}
sb.append(")]");
return sb.toString();
}
}
private static final class OrFilter extends Filter {
private final Predicate left;
private final Predicate right;
private OrFilter(Predicate left, Predicate right) {
this.left = left;
this.right = right;
}
public Filter and(final Predicate other){
return new OrFilter(left, new AndFilter(right, other));
}
@Override
public boolean apply(PredicateContext ctx) {
boolean a = left.apply(ctx);
return a || right.apply(ctx);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[?(");
String l = left.toString();
String r = right.toString();
if(l.startsWith("[?(")){
l = l.substring(3, l.length() - 2);
}
if(r.startsWith("[?(")){
r = r.substring(3, r.length() - 2);
}
sb.append(l).append(" || ").append(r);
sb.append(")]");
return sb.toString();
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/InvalidCriteriaException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
@SuppressWarnings("serial")
public class InvalidCriteriaException extends JsonPathException {
public InvalidCriteriaException() {
}
public InvalidCriteriaException(String message) {
super(message);
}
public InvalidCriteriaException(String message, Throwable cause) {
super(message, cause);
}
public InvalidCriteriaException(Throwable cause) {
super(cause);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/InvalidJsonException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
@SuppressWarnings("serial")
public class InvalidJsonException extends JsonPathException {
/**
* Problematic JSON if available.
*/
private final String json;
public InvalidJsonException() {
json = null;
}
public InvalidJsonException(String message) {
super(message);
json = null;
}
public InvalidJsonException(String message, Throwable cause) {
super(message, cause);
json = null;
}
public InvalidJsonException(Throwable cause) {
super(cause);
json = null;
}
/**
* Rethrow the exception with the problematic JSON captured.
*/
public InvalidJsonException(final Throwable cause, final String json) {
super(cause);
this.json = json;
}
/**
* @return the problematic JSON if available.
*/
public String getJson() {
return json;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/InvalidModificationException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public class InvalidModificationException extends JsonPathException {
public InvalidModificationException(String message) {
super(message);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/InvalidPathException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
@SuppressWarnings("serial")
public class InvalidPathException extends JsonPathException {
public InvalidPathException() {
}
public InvalidPathException(String message) {
super(message);
}
public InvalidPathException(String message, Throwable cause) {
super(message, cause);
}
public InvalidPathException(Throwable cause) {
super(cause);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/JsonPath.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.internal.*;
import com.jayway.jsonpath.internal.path.PathCompiler;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import static com.jayway.jsonpath.Option.ALWAYS_RETURN_LIST;
import static com.jayway.jsonpath.Option.AS_PATH_LIST;
import static com.jayway.jsonpath.internal.Utils.*;
/**
*
* JsonPath is to JSON what XPATH is to XML, a simple way to extract parts of a given document. JsonPath is
* available in many programming languages such as Javascript, Python and PHP.
*
* JsonPath allows you to compile a json path string to use it many times or to compile and apply in one
* single on demand operation.
*
* Given the Json document:
*
*
* String json =
* "{
* "store":
* {
* "book":
* [
* {
* "category": "reference",
* "author": "Nigel Rees",
* "title": "Sayings of the Century",
* "price": 8.95
* },
* {
* "category": "fiction",
* "author": "Evelyn Waugh",
* "title": "Sword of Honour",
* "price": 12.99
* }
* ],
* "bicycle":
* {
* "color": "red",
* "price": 19.95
* }
* }
* }";
*
*
* A JsonPath can be compiled and used as shown:
*
*
* JsonPath path = JsonPath.compile("$.store.book[1]");
*
* List<Object> books = path.read(json);
*
*
* Or:
*
*
* List<Object> authors = JsonPath.read(json, "$.store.book[*].author")
*
*
* If the json path returns a single value (is definite):
*
*
* String author = JsonPath.read(json, "$.store.book[1].author")
*
*/
public class JsonPath {
private final Path path;
private JsonPath(String jsonPath, Predicate[] filters) {
notNull(jsonPath, "path can not be null");
this.path = PathCompiler.compile(jsonPath, filters);
}
/**
* Returns the string representation of this JsonPath
*
* @return path as String
*/
public String getPath() {
return this.path.toString();
}
/**
* @see JsonPath#isDefinite()
*/
public static boolean isPathDefinite(String path) {
return compile(path).isDefinite();
}
/**
* Checks if a path points to a single item or if it potentially returns multiple items
*
* a path is considered not definite if it contains a scan fragment ".."
* or an array position fragment that is not based on a single index
*
*
* definite path examples are:
*
* $store.book
* $store.book[1].title
*
* not definite path examples are:
*
* $..book
* $.store.book[*]
* $.store.book[1,2]
* $.store.book[?(@.category = 'fiction')]
*
* @return true if path is definite (points to single item)
*/
public boolean isDefinite() {
return path.isDefinite();
}
/**
* Applies this JsonPath to the provided json document.
* Note that the document must be identified as either a List or Map by
* the {@link JsonProvider}
*
* @param jsonObject a container Object
* @param expected return type
* @return object(s) matched by the given path
*/
@SuppressWarnings({"unchecked"})
public T read(Object jsonObject) {
return read(jsonObject, Configuration.defaultConfiguration());
}
/**
* Applies this JsonPath to the provided json document.
* Note that the document must be identified as either a List or Map by
* the {@link JsonProvider}
*
* @param jsonObject a container Object
* @param configuration configuration to use
* @param expected return type
* @return object(s) matched by the given path
*/
@SuppressWarnings("unchecked")
public T read(Object jsonObject, Configuration configuration) {
boolean optAsPathList = configuration.containsOption(AS_PATH_LIST);
boolean optAlwaysReturnList = configuration.containsOption(Option.ALWAYS_RETURN_LIST);
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
if (path.isFunctionPath()) {
if (optAsPathList || optAlwaysReturnList) {
if (optSuppressExceptions) {
return (T) (path.isDefinite() ? null : configuration.jsonProvider().createArray());
}
throw new JsonPathException("Options " + AS_PATH_LIST + " and " + ALWAYS_RETURN_LIST + " are not allowed when using path functions!");
}
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration);
if (optSuppressExceptions && evaluationContext.getPathList().isEmpty()) {
return (T) (path.isDefinite() ? null : configuration.jsonProvider().createArray());
}
return evaluationContext.getValue(true);
} else if (optAsPathList) {
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration);
if (optSuppressExceptions && evaluationContext.getPathList().isEmpty()) {
return (T) configuration.jsonProvider().createArray();
}
return (T) evaluationContext.getPath();
} else {
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration);
if (optSuppressExceptions && evaluationContext.getPathList().isEmpty()) {
if (optAlwaysReturnList) {
return (T) configuration.jsonProvider().createArray();
} else {
return (T) (path.isDefinite() ? null : configuration.jsonProvider().createArray());
}
}
Object res = evaluationContext.getValue(false);
if (optAlwaysReturnList && path.isDefinite()) {
Object array = configuration.jsonProvider().createArray();
configuration.jsonProvider().setArrayIndex(array, 0, res);
return (T) array;
} else {
return (T) res;
}
}
}
/**
* Set the value this path points to in the provided jsonObject
*
* @param jsonObject a json object
* @param configuration configuration to use
* @param expected return type
* @return the updated jsonObject or the path list to updated objects if option AS_PATH_LIST is set.
*/
public T set(Object jsonObject, Object newVal, Configuration configuration) {
notNull(jsonObject, "json can not be null");
notNull(configuration, "configuration can not be null");
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true);
if (evaluationContext.getPathList().isEmpty()) {
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
if (optSuppressExceptions) {
return handleMissingPathInContext(configuration);
} else {
throw new PathNotFoundException();
}
}
for (PathRef updateOperation : evaluationContext.updateOperations()) {
updateOperation.set(newVal, configuration);
}
return resultByConfiguration(jsonObject, configuration, evaluationContext);
}
/**
* Replaces the value on the given path with the result of the {@link MapFunction}.
*
* @param jsonObject a json object
* @param mapFunction Converter object to be invoked
* @param configuration configuration to use
* @return the updated jsonObject or the path list to updated objects if option AS_PATH_LIST is set.
*/
public T map(Object jsonObject, MapFunction mapFunction, Configuration configuration) {
notNull(jsonObject, "json can not be null");
notNull(configuration, "configuration can not be null");
notNull(mapFunction, "mapFunction can not be null");
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true);
if (evaluationContext.getPathList().isEmpty()) {
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
if (optSuppressExceptions) {
return handleMissingPathInContext(configuration);
} else {
throw new PathNotFoundException();
}
}
for (PathRef updateOperation : evaluationContext.updateOperations()) {
updateOperation.convert(mapFunction, configuration);
}
return resultByConfiguration(jsonObject, configuration, evaluationContext);
}
/**
* Deletes the object this path points to in the provided jsonObject
*
* @param jsonObject a json object
* @param configuration configuration to use
* @param expected return type
* @return the updated jsonObject or the path list to deleted objects if option AS_PATH_LIST is set.
*/
public T delete(Object jsonObject, Configuration configuration) {
notNull(jsonObject, "json can not be null");
notNull(configuration, "configuration can not be null");
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true);
if (evaluationContext.getPathList().isEmpty()) {
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
if (optSuppressExceptions) {
return handleMissingPathInContext(configuration);
} else {
throw new PathNotFoundException();
}
}
for (PathRef updateOperation : evaluationContext.updateOperations()) {
updateOperation.delete(configuration);
}
return resultByConfiguration(jsonObject, configuration, evaluationContext);
}
/**
* Adds a new value to the Array this path points to in the provided jsonObject
*
* @param jsonObject a json object
* @param value the value to add
* @param configuration configuration to use
* @param expected return type
* @return the updated jsonObject or the path list to updated object if option AS_PATH_LIST is set.
*/
public T add(Object jsonObject, Object value, Configuration configuration) {
notNull(jsonObject, "json can not be null");
notNull(configuration, "configuration can not be null");
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true);
if (evaluationContext.getPathList().isEmpty()) {
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
if (optSuppressExceptions) {
return handleMissingPathInContext(configuration);
} else {
throw new PathNotFoundException();
}
}
for (PathRef updateOperation : evaluationContext.updateOperations()) {
updateOperation.add(value, configuration);
}
return resultByConfiguration(jsonObject, configuration, evaluationContext);
}
/**
* Adds or updates the Object this path points to in the provided jsonObject with a key with a value
*
* @param jsonObject a json object
* @param key the key to add or update
* @param value the new value
* @param configuration configuration to use
* @param expected return type
* @return the updated jsonObject or the path list to updated objects if option AS_PATH_LIST is set.
*/
public T put(Object jsonObject, String key, Object value, Configuration configuration) {
notNull(jsonObject, "json can not be null");
notEmpty(key, "key can not be null or empty");
notNull(configuration, "configuration can not be null");
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true);
if (evaluationContext.getPathList().isEmpty()) {
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
if (optSuppressExceptions) {
return handleMissingPathInContext(configuration);
} else {
throw new PathNotFoundException();
}
}
for (PathRef updateOperation : evaluationContext.updateOperations()) {
updateOperation.put(key, value, configuration);
}
return resultByConfiguration(jsonObject, configuration, evaluationContext);
}
public T renameKey(Object jsonObject, String oldKeyName, String newKeyName, Configuration configuration) {
notNull(jsonObject, "json can not be null");
notEmpty(newKeyName, "newKeyName can not be null or empty");
notNull(configuration, "configuration can not be null");
EvaluationContext evaluationContext = path.evaluate(jsonObject, jsonObject, configuration, true);
for (PathRef updateOperation : evaluationContext.updateOperations()) {
boolean optSuppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
try {
updateOperation.renameKey(oldKeyName, newKeyName, configuration);
} catch (RuntimeException e) {
if(!optSuppressExceptions){
throw e;
}else{
// With option SUPPRESS_EXCEPTIONS,
// the PathNotFoundException should be ignored and the other updateOperation should be continued.
}
}
}
return resultByConfiguration(jsonObject, configuration, evaluationContext);
}
/**
* Applies this JsonPath to the provided json string
*
* @param json a json string
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public T read(String json) {
return read(json, Configuration.defaultConfiguration());
}
/**
* Applies this JsonPath to the provided json string
*
* @param json a json string
* @param configuration configuration to use
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public T read(String json, Configuration configuration) {
notEmpty(json, "json can not be null or empty");
notNull(configuration, "jsonProvider can not be null");
return read(configuration.jsonProvider().parse(json), configuration);
}
/**
* Applies this JsonPath to the provided json URL
*
* @param jsonURL url to read from
* @param expected return type
* @return list of objects matched by the given path
* @throws IOException
*/
@SuppressWarnings({"unchecked"})
public T read(URL jsonURL) throws IOException {
return read(jsonURL, Configuration.defaultConfiguration());
}
/**
* Applies this JsonPath to the provided json file
*
* @param jsonFile file to read from
* @param expected return type
* @return list of objects matched by the given path
* @throws IOException
*/
@SuppressWarnings({"unchecked"})
public T read(File jsonFile) throws IOException {
return read(jsonFile, Configuration.defaultConfiguration());
}
/**
* Applies this JsonPath to the provided json file
*
* @param jsonFile file to read from
* @param configuration configuration to use
* @param expected return type
* @return list of objects matched by the given path
* @throws IOException
*/
@SuppressWarnings({"unchecked"})
public T read(File jsonFile, Configuration configuration) throws IOException {
notNull(jsonFile, "json file can not be null");
isTrue(jsonFile.exists(), "json file does not exist");
notNull(configuration, "jsonProvider can not be null");
FileInputStream fis = null;
try {
fis = new FileInputStream(jsonFile);
return read(fis, configuration);
} finally {
Utils.closeQuietly(fis);
}
}
/**
* Applies this JsonPath to the provided json input stream
*
* @param jsonInputStream input stream to read from
* @param expected return type
* @return list of objects matched by the given path
* @throws IOException
*/
@SuppressWarnings({"unchecked"})
public T read(InputStream jsonInputStream) throws IOException {
return read(jsonInputStream, Configuration.defaultConfiguration());
}
/**
* Applies this JsonPath to the provided json input stream
*
* @param jsonInputStream input stream to read from
* @param configuration configuration to use
* @param expected return type
* @return list of objects matched by the given path
* @throws IOException
*/
@SuppressWarnings({"unchecked"})
public T read(InputStream jsonInputStream, Configuration configuration) throws IOException {
notNull(jsonInputStream, "json input stream can not be null");
notNull(configuration, "configuration can not be null");
return read(jsonInputStream, "UTF-8", configuration);
}
/**
* Applies this JsonPath to the provided json input stream
*
* @param jsonInputStream input stream to read from
* @param configuration configuration to use
* @param expected return type
* @return list of objects matched by the given path
* @throws IOException
*/
@SuppressWarnings({"unchecked"})
public T read(InputStream jsonInputStream, String charset, Configuration configuration) throws IOException {
notNull(jsonInputStream, "json input stream can not be null");
notNull(charset, "charset can not be null");
notNull(configuration, "configuration can not be null");
try {
return read(configuration.jsonProvider().parse(jsonInputStream, charset), configuration);
} finally {
Utils.closeQuietly(jsonInputStream);
}
}
// --------------------------------------------------------
//
// Static factory methods
//
// --------------------------------------------------------
/**
* Compiles a JsonPath
*
* @param jsonPath to compile
* @param filters filters to be applied to the filter place holders [?] in the path
* @return compiled JsonPath
*/
public static JsonPath compile(String jsonPath, Predicate... filters) {
notEmpty(jsonPath, "json can not be null or empty");
return new JsonPath(jsonPath, filters);
}
// --------------------------------------------------------
//
// Static utility functions
//
// --------------------------------------------------------
/**
* Creates a new JsonPath and applies it to the provided Json object
*
* @param json a json object
* @param jsonPath the json path
* @param filters filters to be applied to the filter place holders [?] in the path
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static T read(Object json, String jsonPath, Predicate... filters) {
return parse(json).read(jsonPath, filters);
}
/**
* Creates a new JsonPath and applies it to the provided Json string
*
* @param json a json string
* @param jsonPath the json path
* @param filters filters to be applied to the filter place holders [?] in the path
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static T read(String json, String jsonPath, Predicate... filters) {
return new ParseContextImpl().parse(json).read(jsonPath, filters);
}
/**
* Creates a new JsonPath and applies it to the provided Json object
*
* @param jsonURL url pointing to json doc
* @param jsonPath the json path
* @param filters filters to be applied to the filter place holders [?] in the path
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
@Deprecated
public static T read(URL jsonURL, String jsonPath, Predicate... filters) throws IOException {
return new ParseContextImpl().parse(jsonURL).read(jsonPath, filters);
}
/**
* Creates a new JsonPath and applies it to the provided Json object
*
* @param jsonFile json file
* @param jsonPath the json path
* @param filters filters to be applied to the filter place holders [?] in the path
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static T read(File jsonFile, String jsonPath, Predicate... filters) throws IOException {
return new ParseContextImpl().parse(jsonFile).read(jsonPath, filters);
}
/**
* Creates a new JsonPath and applies it to the provided Json object
*
* @param jsonInputStream json input stream
* @param jsonPath the json path
* @param filters filters to be applied to the filter place holders [?] in the path
* @param expected return type
* @return list of objects matched by the given path
*/
@SuppressWarnings({"unchecked"})
public static T read(InputStream jsonInputStream, String jsonPath, Predicate... filters) throws IOException {
return new ParseContextImpl().parse(jsonInputStream).read(jsonPath, filters);
}
// --------------------------------------------------------
//
// Static Fluent API
//
// --------------------------------------------------------
/**
* Creates a {@link ParseContext} that can be used to parse JSON input. The parse context
* is as thread safe as the underlying {@link JsonProvider}. Note that not all JsonProvider are
* thread safe.
*
* @param configuration configuration to use when parsing JSON
* @return a parsing context based on given configuration
*/
public static ParseContext using(Configuration configuration) {
return new ParseContextImpl(configuration);
}
/**
* Creates a {@link ParseContext} that will parse a given JSON input.
*
* @param provider jsonProvider to use when parsing JSON
* @return a parsing context based on given jsonProvider
*/
@Deprecated
public static ParseContext using(JsonProvider provider) {
return new ParseContextImpl(Configuration.builder().jsonProvider(provider).build());
}
/**
* Parses the given JSON input using the default {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json input
* @return a read context
*/
public static DocumentContext parse(Object json) {
return new ParseContextImpl().parse(json);
}
/**
* Parses the given JSON input using the default {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json string
* @return a read context
*/
public static DocumentContext parse(String json) {
return new ParseContextImpl().parse(json);
}
/**
* Parses the given JSON input using the default {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json stream
* @return a read context
*/
public static DocumentContext parse(InputStream json) {
return new ParseContextImpl().parse(json);
}
/**
* Parses the given JSON input using the default {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json file
* @return a read context
*/
public static DocumentContext parse(File json) throws IOException {
return new ParseContextImpl().parse(json);
}
/**
* Parses the given JSON input using the default {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json url
* @return a read context
*/
@Deprecated
public static DocumentContext parse(URL json) throws IOException {
return new ParseContextImpl().parse(json);
}
/**
* Parses the given JSON input using the provided {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json input
* @return a read context
*/
public static DocumentContext parse(Object json, Configuration configuration) {
return new ParseContextImpl(configuration).parse(json);
}
/**
* Parses the given JSON input using the provided {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json input
* @return a read context
*/
public static DocumentContext parse(String json, Configuration configuration) {
return new ParseContextImpl(configuration).parse(json);
}
/**
* Parses the given JSON input using the provided {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json input
* @return a read context
*/
public static DocumentContext parse(InputStream json, Configuration configuration) {
return new ParseContextImpl(configuration).parse(json);
}
/**
* Parses the given JSON input using the provided {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json input
* @return a read context
*/
public static DocumentContext parse(File json, Configuration configuration) throws IOException {
return new ParseContextImpl(configuration).parse(json);
}
/**
* Parses the given JSON input using the provided {@link Configuration} and
* returns a {@link DocumentContext} for path evaluation
*
* @param json input
* @return a read context
*/
@Deprecated
public static DocumentContext parse(URL json, Configuration configuration) throws IOException {
return new ParseContextImpl(configuration).parse(json);
}
private T resultByConfiguration(Object jsonObject, Configuration configuration, EvaluationContext evaluationContext) {
if(configuration.containsOption(AS_PATH_LIST)){
return (T)evaluationContext.getPathList();
} else {
return (T) jsonObject;
}
}
private T handleMissingPathInContext(final Configuration configuration) {
boolean optAsPathList = configuration.containsOption(AS_PATH_LIST);
boolean optAlwaysReturnList = configuration.containsOption(Option.ALWAYS_RETURN_LIST);
if (optAsPathList) {
return (T) configuration.jsonProvider().createArray();
} else {
if (optAlwaysReturnList) {
return (T) configuration.jsonProvider().createArray();
} else {
return (T) (path.isDefinite() ? null : configuration.jsonProvider().createArray());
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/JsonPathException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public class JsonPathException extends RuntimeException {
public JsonPathException() {
}
public JsonPathException(String message) {
super(message);
}
public JsonPathException(String message, Throwable cause) {
super(message, cause);
}
public JsonPathException(Throwable cause) {
super(cause);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/MapFunction.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
/**
* Returns a new representation for the input value.
*/
public interface MapFunction {
Object map(Object currentValue, Configuration configuration);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/Option.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public enum Option {
/**
* returns null for missing leaf.
*
*
* [
* {
* "foo" : "foo1",
* "bar" : "bar1"
* }
* {
* "foo" : "foo2"
* }
* ]
*
*
* the path :
*
* "$[*].bar"
*
* Without flag ["bar1"] is returned
* With flag ["bar1", null] is returned
*
*
*/
DEFAULT_PATH_LEAF_TO_NULL,
/**
* Makes this implementation more compliant to the Goessner spec. All results are returned as Lists.
*/
ALWAYS_RETURN_LIST,
/**
* Returns a list of path strings representing the path of the evaluation hits
*/
AS_PATH_LIST,
/**
* Suppress all exceptions when evaluating path.
*
* If an exception is thrown and the option {@link Option#ALWAYS_RETURN_LIST} an empty list is returned.
* If an exception is thrown and the option {@link Option#ALWAYS_RETURN_LIST} is not present null is returned.
*/
SUPPRESS_EXCEPTIONS,
/**
* Configures JsonPath to require properties defined in path when an indefinite path is evaluated.
*
*
* Given:
*
*
* [
* {
* "a" : "a-val",
* "b" : "b-val"
* },
* {
* "a" : "a-val",
* }
* ]
*
*
* evaluating the path "$[*].b"
*
* If REQUIRE_PROPERTIES option is present PathNotFoundException is thrown.
* If REQUIRE_PROPERTIES option is not present ["b-val"] is returned.
*/
REQUIRE_PROPERTIES
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/ParseContext.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* Parses JSON as specified by the used {@link com.jayway.jsonpath.spi.json.JsonProvider}.
*/
public interface ParseContext {
DocumentContext parse(String json);
DocumentContext parse(Object json);
DocumentContext parse(InputStream json);
DocumentContext parse(InputStream json, String charset);
DocumentContext parse(File json) throws IOException;
DocumentContext parseUtf8(byte[] json);
@Deprecated
DocumentContext parse(URL json) throws IOException;
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/PathNotFoundException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public class PathNotFoundException extends InvalidPathException {
public PathNotFoundException() {
}
public PathNotFoundException(String message) {
super(message);
}
public PathNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public PathNotFoundException(Throwable cause) {
super(cause);
}
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/Predicate.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import com.jayway.jsonpath.spi.mapper.MappingException;
/**
*
*/
public interface Predicate {
boolean apply(PredicateContext ctx);
public interface PredicateContext {
/**
* Returns the current item being evaluated by this predicate
* @return current document
*/
Object item();
/**
* Returns the current item being evaluated by this predicate. It will be mapped
* to the provided class
* @return current document
*/
T item(Class clazz) throws MappingException;
/**
* Returns the root document (the complete JSON)
* @return root document
*/
Object root();
/**
* Configuration to use when evaluating
* @return configuration
*/
Configuration configuration();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/ReadContext.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public interface ReadContext {
/**
* Returns the configuration used for reading
*
* @return an immutable configuration
*/
Configuration configuration();
/**
* Returns the JSON model that this context is operating on
*
* @return json model
*/
T json();
/**
* Returns the JSON model that this context is operating on as a JSON string
*
* @return json model as string
*/
String jsonString();
/**
* Reads the given path from this context
*
* @param path path to read
* @param filters filters
* @param
* @return result
*/
T read(String path, Predicate... filters);
/**
* Reads the given path from this context
*
* @param path path to read
* @param type expected return type (will try to map)
* @param filters filters
* @param
* @return result
*/
T read(String path, Class type, Predicate... filters);
/**
* Reads the given path from this context
*
* @param path path to apply
* @param
* @return result
*/
T read(JsonPath path);
/**
* Reads the given path from this context
*
* @param path path to apply
* @param type expected return type (will try to map)
* @param
* @return result
*/
T read(JsonPath path, Class type);
/**
* Reads the given path from this context
*
* Sample code to create a TypeRef
*
* TypeRef ref = new TypeRef>() {};
*
*
* @param path path to apply
* @param typeRef expected return type (will try to map)
* @param
* @return result
*/
T read(JsonPath path, TypeRef typeRef);
/**
* Reads the given path from this context
*
* Sample code to create a TypeRef
*
* TypeRef ref = new TypeRef>() {};
*
*
* @param path path to apply
* @param typeRef expected return type (will try to map)
* @param
* @return result
*/
T read(String path, TypeRef typeRef);
/**
* Stops evaluation when maxResults limit has been reached
* @param maxResults
* @return the read context
*/
ReadContext limit(int maxResults);
/**
* Adds listener to the evaluation of this path
* @param listener listeners to add
* @return the read context
*/
ReadContext withListeners(EvaluationListener... listener);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/TypeRef.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Used to specify generic type information in {@link com.jayway.jsonpath.ReadContext}
*
*
* TypeRef ref = new TypeRef>() { };
*
*
* @param
*/
public abstract class TypeRef implements Comparable> {
protected final Type type;
protected TypeRef()
{
Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof Class>) {
throw new IllegalArgumentException("No type info in TypeRef");
}
type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() { return type; }
/**
* The only reason we define this method (and require implementation
* of Comparable) is to prevent constructing a
* reference without type information.
*/
@Override
public int compareTo(TypeRef o) {
return 0;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/ValueCompareException.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public class ValueCompareException extends JsonPathException {
public ValueCompareException() {
}
/**
* Construct the exception with message capturing the classes for two objects.
*
* @param left first object
* @param right second object
*/
public ValueCompareException(final Object left, final Object right) {
super(String.format("Can not compare a %1s with a %2s", left.getClass().getName(), right.getClass().getName()));
}
public ValueCompareException(String message) {
super(message);
}
public ValueCompareException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/WriteContext.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath;
public interface WriteContext {
/**
* Returns the configuration used for reading
*
* @return an immutable configuration
*/
Configuration configuration();
/**
* Returns the JSON model that this context is operating on
*
* @return json model
*/
T json();
/**
* Returns the JSON model that this context is operating on as a JSON string
*
* @return json model as string
*/
String jsonString();
/**
* Set the value a the given path
*
* @param path path to set
* @param newValue new value
* @param filters filters
* @return a document context
*/
DocumentContext set(String path, Object newValue, Predicate... filters);
/**
* Set the value a the given path
*
* @param path path to set
* @param newValue new value
* @return a document context
*/
DocumentContext set(JsonPath path, Object newValue);
/**
* Replaces the value on the given path with the result of the {@link MapFunction}.
*
* @param path path to be converted set
* @param mapFunction Converter object to be invoked
* @param filters filters
* @return a document context
*/
DocumentContext map(String path, MapFunction mapFunction, Predicate... filters);
/**
* Replaces the value on the given path with the result of the {@link MapFunction}.
*
* @param path path to be converted set
* @param mapFunction Converter object to be invoked (or lambda:))
* @return a document context
*/
DocumentContext map(JsonPath path, MapFunction mapFunction);
/**
* Deletes the given path
*
* @param path path to delete
* @param filters filters
* @return a document context
*/
DocumentContext delete(String path, Predicate... filters);
/**
* Deletes the given path
*
* @param path path to delete
* @return a document context
*/
DocumentContext delete(JsonPath path);
/**
* Add value to array
*
*
*
* List array = new ArrayList(){{
* add(0);
* add(1);
* }};
*
* JsonPath.parse(array).add("$", 2);
*
* assertThat(array).containsExactly(0,1,2);
*
*
*
* @param path path to array
* @param value value to add
* @param filters filters
* @return a document context
*/
DocumentContext add(String path, Object value, Predicate... filters);
/**
* Add value to array at the given path
*
* @param path path to array
* @param value value to add
* @return a document context
*/
DocumentContext add(JsonPath path, Object value);
/**
* Add or update the key with a the given value at the given path
*
* @param path path to object
* @param key key to add
* @param value value of key
* @param filters filters
* @return a document context
*/
DocumentContext put(String path, String key, Object value, Predicate... filters);
/**
* Add or update the key with a the given value at the given path
*
* @param path path to array
* @param key key to add
* @param value value of key
* @return a document context
*/
DocumentContext put(JsonPath path, String key, Object value);
/**
* Renames the last key element of a given path.
* @param path The path to the old key. Should be resolved to a map
* or an array including map items.
* @param oldKeyName The old key name.
* @param newKeyName The new key name.
* @param filters filters.
* @return a document content.
*/
DocumentContext renameKey(String path, String oldKeyName, String newKeyName, Predicate... filters);
/**
* Renames the last key element of a given path.
* @param path The path to the old key. Should be resolved to a map
* or an array including map items.
* @param oldKeyName The old key name.
* @param newKeyName The new key name.
* @return a document content.
*/
DocumentContext renameKey(JsonPath path, String oldKeyName, String newKeyName);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java
================================================
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.InvalidPathException;
public class CharacterIndex {
private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_PARENTHESIS = ')';
private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char SPACE = ' ';
private static final char ESCAPE = '\\';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final char MINUS = '-';
private static final char PERIOD = '.';
private static final char REGEX = '/';
//workaround for issue: https://github.com/json-path/JsonPath/issues/590
private static final char SCI_E = 'E';
private static final char SCI_e = 'e';
private final CharSequence charSequence;
private int position;
private int endPosition;
public CharacterIndex(CharSequence charSequence) {
this.charSequence = charSequence;
this.position = 0;
this.endPosition = charSequence.length() - 1;
}
public int length() {
return endPosition + 1;
}
public char charAt(int idx) {
return charSequence.charAt(idx);
}
public char currentChar() {
return charSequence.charAt(position);
}
public boolean currentCharIs(char c) {
return (charSequence.charAt(position) == c);
}
public boolean lastCharIs(char c) {
return charSequence.charAt(endPosition) == c;
}
public boolean nextCharIs(char c) {
return inBounds(position + 1) && (charSequence.charAt(position + 1) == c);
}
public int incrementPosition(int charCount) {
return setPosition(position + charCount);
}
public int decrementEndPosition(int charCount) {
return setEndPosition(endPosition - charCount);
}
public int setPosition(int newPosition) {
//position = min(newPosition, charSequence.length() - 1);
position = newPosition;
return position;
}
private int setEndPosition(int newPosition) {
endPosition = newPosition;
return endPosition;
}
public int position(){
return position;
}
public int indexOfClosingSquareBracket(int startPosition) {
int readPosition = startPosition;
while (inBounds(readPosition)) {
if(charAt(readPosition) == CLOSE_SQUARE_BRACKET){
return readPosition;
}
readPosition++;
}
return -1;
}
public int indexOfMatchingCloseChar(int startPosition, char openChar, char closeChar, boolean skipStrings, boolean skipRegex) {
if(charAt(startPosition) != openChar){
throw new InvalidPathException("Expected " + openChar + " but found " + charAt(startPosition));
}
int opened = 1;
int readPosition = startPosition + 1;
while (inBounds(readPosition)) {
if (skipStrings) {
char quoteChar = charAt(readPosition);
if (quoteChar == SINGLE_QUOTE || quoteChar == DOUBLE_QUOTE){
readPosition = nextIndexOfUnescaped(readPosition, quoteChar);
if(readPosition == -1){
throw new InvalidPathException("Could not find matching close quote for " + quoteChar + " when parsing : " + charSequence);
}
readPosition++;
}
}
if (skipRegex) {
if (charAt(readPosition) == REGEX) {
readPosition = nextIndexOfUnescaped(readPosition, REGEX);
if(readPosition == -1){
throw new InvalidPathException("Could not find matching close for " + REGEX + " when parsing regex in : " + charSequence);
}
readPosition++;
}
}
if (charAt(readPosition) == openChar) {
opened++;
}
if (charAt(readPosition) == closeChar) {
opened--;
if (opened == 0) {
return readPosition;
}
}
readPosition++;
}
return -1;
}
public int indexOfClosingBracket(int startPosition, boolean skipStrings, boolean skipRegex) {
return indexOfMatchingCloseChar(startPosition, OPEN_PARENTHESIS, CLOSE_PARENTHESIS, skipStrings, skipRegex);
}
public int indexOfNextSignificantChar(char c) {
return indexOfNextSignificantChar(position, c);
}
public int indexOfNextSignificantChar(int startPosition, char c) {
int readPosition = startPosition + 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition++;
}
if (charAt(readPosition) == c) {
return readPosition;
} else {
return -1;
}
}
public int nextIndexOf(char c) {
return nextIndexOf(position + 1, c);
}
public int nextIndexOf(int startPosition, char c) {
int readPosition = startPosition;
while (!isOutOfBounds(readPosition)) {
if (charAt(readPosition) == c) {
return readPosition;
}
readPosition++;
}
return -1;
}
public int nextIndexOfUnescaped(char c) {
return nextIndexOfUnescaped(position, c);
}
public int nextIndexOfUnescaped(int startPosition, char c) {
int readPosition = startPosition + 1;
boolean inEscape = false;
while (!isOutOfBounds(readPosition)) {
if(inEscape){
inEscape = false;
} else if('\\' == charAt(readPosition)){
inEscape = true;
} else if (c == charAt(readPosition)){
return readPosition;
}
readPosition ++;
}
return -1;
}
public char charAtOr(int postition, char defaultChar){
if(!inBounds(postition)) return defaultChar;
else return charAt(postition);
}
public boolean nextSignificantCharIs(int startPosition, char c) {
int readPosition = startPosition + 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition++;
}
return !isOutOfBounds(readPosition) && charAt(readPosition) == c;
}
public boolean nextSignificantCharIs(char c) {
return nextSignificantCharIs(position, c);
}
public char nextSignificantChar() {
return nextSignificantChar(position);
}
public char nextSignificantChar(int startPosition) {
int readPosition = startPosition + 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition++;
}
if (!isOutOfBounds(readPosition)) {
return charAt(readPosition);
} else {
return ' ';
}
}
public void readSignificantChar(char c) {
if (skipBlanks().currentChar() != c) {
throw new InvalidPathException(String.format("Expected character: %c", c));
}
incrementPosition(1);
}
public boolean hasSignificantSubSequence(CharSequence s) {
skipBlanks();
if (! inBounds(position + s.length() - 1)) {
return false;
}
if (! subSequence(position, position + s.length()).equals(s)) {
return false;
}
incrementPosition(s.length());
return true;
}
public int indexOfPreviousSignificantChar(int startPosition){
int readPosition = startPosition - 1;
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) {
readPosition--;
}
if (!isOutOfBounds(readPosition)) {
return readPosition;
} else {
return -1;
}
}
public int indexOfPreviousSignificantChar(){
return indexOfPreviousSignificantChar(position);
}
public char previousSignificantChar(int startPosition) {
int previousSignificantCharIndex = indexOfPreviousSignificantChar(startPosition);
if(previousSignificantCharIndex == -1) return ' ';
else return charAt(previousSignificantCharIndex);
}
public char previousSignificantChar() {
return previousSignificantChar(position);
}
public boolean currentIsTail() {
return position >= endPosition;
}
public boolean hasMoreCharacters() {
return inBounds(position + 1);
}
public boolean inBounds(int idx) {
return (idx >= 0) && (idx <= endPosition);
}
public boolean inBounds() {
return inBounds(position);
}
public boolean isOutOfBounds(int idx) {
return !inBounds(idx);
}
public CharSequence subSequence(int start, int end) {
return charSequence.subSequence(start, end);
}
public CharSequence charSequence() {
return charSequence;
}
@Override
public String toString() {
return charSequence.toString();
}
public boolean isNumberCharacter(int readPosition) {
char c = charAt(readPosition);
//workaround for issue: https://github.com/json-path/JsonPath/issues/590
return Character.isDigit(c) || c == MINUS || c == PERIOD || c == SCI_E || c == SCI_e;
}
public CharacterIndex skipBlanks() {
while (inBounds() && position < endPosition && currentChar() == SPACE){
incrementPosition(1);
}
return this;
}
private CharacterIndex skipBlanksAtEnd() {
while (inBounds() && position < endPosition && lastCharIs(SPACE)){
decrementEndPosition(1);
}
return this;
}
public CharacterIndex trim() {
skipBlanks();
skipBlanksAtEnd();
return this;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java
================================================
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration.Defaults;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.json.JsonSmartJsonProvider;
import com.jayway.jsonpath.spi.mapper.JsonSmartMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;
import java.util.EnumSet;
import java.util.Set;
public final class DefaultsImpl implements Defaults {
public static final DefaultsImpl INSTANCE = new DefaultsImpl();
private final MappingProvider mappingProvider = new JsonSmartMappingProvider();
@Override
public JsonProvider jsonProvider() {
return new JsonSmartJsonProvider();
}
@Override
public Set options() {
return EnumSet.noneOf(Option.class);
}
@Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
private DefaultsImpl() {
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/EvaluationAbortException.java
================================================
package com.jayway.jsonpath.internal;
public class EvaluationAbortException extends RuntimeException {
private static final long serialVersionUID = 4419305302960432348L;
// this is just a marker exception to abort evaluation, we don't care about
// the stack
@Override
public Throwable fillInStackTrace() {
return this;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/EvaluationContext.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
import java.util.Collection;
import java.util.List;
public interface EvaluationContext {
/**
*
* @return the configuration used for this evaluation
*/
Configuration configuration();
/**
* The json document that is evaluated
*
* @return the document
*/
Object rootDocument();
/**
* This method does not adhere to configuration settings. It will return a single object (not wrapped in a List) even if the
* configuration contains the {@link com.jayway.jsonpath.Option#ALWAYS_RETURN_LIST}
*
* @param expected return type
* @return evaluation result
*/
T getValue();
/**
* See {@link com.jayway.jsonpath.internal.EvaluationContext#getValue()}
*
* @param unwrap tells th underlying json provider if primitives should be unwrapped
* @param expected return type
* @return evaluation result
*/
T getValue(boolean unwrap);
/**
* Returns the list of formalized paths that represent the result of the evaluation
* @param
* @return list of paths
*/
T getPath();
/**
* Convenience method to get list of hits as String path representations
*
* @return list of path representations
*/
List getPathList();
Collection updateOperations();
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/JsonContext.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.EvaluationListener;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.MapFunction;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.ReadContext;
import com.jayway.jsonpath.TypeRef;
import com.jayway.jsonpath.spi.cache.Cache;
import com.jayway.jsonpath.spi.cache.CacheProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
import static com.jayway.jsonpath.JsonPath.compile;
import static com.jayway.jsonpath.internal.Utils.notEmpty;
import static com.jayway.jsonpath.internal.Utils.notNull;
public class JsonContext implements DocumentContext {
private static final Logger logger = LoggerFactory.getLogger(JsonContext.class);
private final Configuration configuration;
private final Object json;
JsonContext(Object json, Configuration configuration) {
notNull(json, "json can not be null");
notNull(configuration, "configuration can not be null");
this.configuration = configuration;
this.json = json;
}
@Override
public Configuration configuration() {
return configuration;
}
//------------------------------------------------
//
// ReadContext impl
//
//------------------------------------------------
@Override
public Object json() {
return json;
}
@Override
public String jsonString() {
return configuration.jsonProvider().toJson(json);
}
@Override
public T read(String path, Predicate... filters) {
notEmpty(path, "path can not be null or empty");
return read(pathFromCache(path, filters));
}
@Override
public T read(String path, Class type, Predicate... filters) {
return convert(read(path, filters), type, configuration);
}
@Override
public T read(JsonPath path) {
notNull(path, "path can not be null");
return path.read(json, configuration);
}
@Override
public T read(JsonPath path, Class type) {
return convert(read(path), type, configuration);
}
@Override
public T read(JsonPath path, TypeRef type) {
return convert(read(path), type, configuration);
}
@Override
public T read(String path, TypeRef type) {
return convert(read(path), type, configuration);
}
@Override
public ReadContext limit(int maxResults) {
return withListeners(new LimitingEvaluationListener(maxResults));
}
@Override
public ReadContext withListeners(EvaluationListener... listener) {
return new JsonContext(json, configuration.setEvaluationListeners(listener));
}
private T convert(Object obj, Class targetType, Configuration configuration) {
return configuration.mappingProvider().map(obj, targetType, configuration);
}
private T convert(Object obj, TypeRef targetType, Configuration configuration) {
return configuration.mappingProvider().map(obj, targetType, configuration);
}
@Override
public DocumentContext set(String path, Object newValue, Predicate... filters) {
return set(pathFromCache(path, filters), newValue);
}
@Override
public DocumentContext set(JsonPath path, Object newValue) {
List modified = path.set(json, newValue, configuration.addOptions(Option.AS_PATH_LIST));
if (logger.isDebugEnabled()) {
for (String p : modified) {
logger.debug("Set path {} new value {}", p, newValue);
}
}
return this;
}
@Override
public DocumentContext map(String path, MapFunction mapFunction, Predicate... filters) {
map(pathFromCache(path, filters), mapFunction);
return this;
}
@Override
public DocumentContext map(JsonPath path, MapFunction mapFunction) {
Object obj = path.map(json, mapFunction, configuration);
return obj==null ? null:this;
}
@Override
public DocumentContext delete(String path, Predicate... filters) {
return delete(pathFromCache(path, filters));
}
@Override
public DocumentContext delete(JsonPath path) {
List modified = path.delete(json, configuration.addOptions(Option.AS_PATH_LIST));
if (logger.isDebugEnabled()) {
for (String p : modified) {
logger.debug("Delete path {}", p);
}
}
return this;
}
@Override
public DocumentContext add(String path, Object value, Predicate... filters) {
return add(pathFromCache(path, filters), value);
}
@Override
public DocumentContext add(JsonPath path, Object value) {
List modified = path.add(json, value, configuration.addOptions(Option.AS_PATH_LIST));
if (logger.isDebugEnabled()) {
for (String p : modified) {
logger.debug("Add path {} new value {}", p, value);
}
}
return this;
}
@Override
public DocumentContext put(String path, String key, Object value, Predicate... filters) {
return put(pathFromCache(path, filters), key, value);
}
@Override
public DocumentContext renameKey(String path, String oldKeyName, String newKeyName, Predicate... filters) {
return renameKey(pathFromCache(path, filters), oldKeyName, newKeyName);
}
@Override
public DocumentContext renameKey(JsonPath path, String oldKeyName, String newKeyName) {
List modified = path.renameKey(json, oldKeyName, newKeyName, configuration.addOptions(Option.AS_PATH_LIST));
if (logger.isDebugEnabled()) {
for (String p : modified) {
logger.debug("Rename path {} new value {}", p, newKeyName);
}
}
return this;
}
@Override
public DocumentContext put(JsonPath path, String key, Object value) {
List modified = path.put(json, key, value, configuration.addOptions(Option.AS_PATH_LIST));
if (logger.isDebugEnabled()) {
for (String p : modified) {
logger.debug("Put path {} key {} value {}", p, key, value);
}
}
return this;
}
private JsonPath pathFromCache(String path, Predicate[] filters) {
Cache cache = CacheProvider.getCache();
String cacheKey = filters == null || filters.length == 0
? path : Utils.concat(path, Arrays.toString(filters));
JsonPath jsonPath = cache.get(cacheKey);
if (jsonPath == null) {
jsonPath = compile(path, filters);
cache.put(cacheKey, jsonPath);
}
return jsonPath;
}
private final static class LimitingEvaluationListener implements EvaluationListener {
final int limit;
private LimitingEvaluationListener(int limit) {
this.limit = limit;
}
@Override
public EvaluationContinuation resultFound(FoundResult found) {
if (found.index() == limit - 1) {
return EvaluationContinuation.ABORT;
} else {
return EvaluationContinuation.CONTINUE;
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/JsonFormatter.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal;
public class JsonFormatter {
private static final String INDENT = " ";
private static final String NEW_LINE = System.getProperty("line.separator");
private static final int MODE_SINGLE = 100;
private static final int MODE_DOUBLE = 101;
private static final int MODE_ESCAPE_SINGLE = 102;
private static final int MODE_ESCAPE_DOUBLE = 103;
private static final int MODE_BETWEEN = 104;
private static void appendIndent(StringBuilder sb, int count) {
for (; count > 0; --count) sb.append(INDENT);
}
public static String prettyPrint(String input) {
input = input.replaceAll("[\\r\\n]", "");
StringBuilder output = new StringBuilder(input.length() * 2);
int mode = MODE_BETWEEN;
int depth = 0;
for (int i = 0; i < input.length(); ++i) {
char ch = input.charAt(i);
switch (mode) {
case MODE_BETWEEN:
switch (ch) {
case '{':
case '[':
output.append(ch);
output.append(NEW_LINE);
appendIndent(output, ++depth);
break;
case '}':
case ']':
output.append(NEW_LINE);
appendIndent(output, --depth);
output.append(ch);
break;
case ',':
output.append(ch);
output.append(NEW_LINE);
appendIndent(output, depth);
break;
case ':':
output.append(" : ");
break;
case '\'':
output.append(ch);
mode = MODE_SINGLE;
break;
case '"':
output.append(ch);
mode = MODE_DOUBLE;
break;
case ' ':
break;
default:
output.append(ch);
break;
}
break;
case MODE_ESCAPE_SINGLE:
output.append(ch);
mode = MODE_SINGLE;
break;
case MODE_ESCAPE_DOUBLE:
output.append(ch);
mode = MODE_DOUBLE;
break;
case MODE_SINGLE:
output.append(ch);
switch (ch) {
case '\'':
mode = MODE_BETWEEN;
break;
case '\\':
mode = MODE_ESCAPE_SINGLE;
break;
}
break;
case MODE_DOUBLE:
output.append(ch);
switch (ch) {
case '"':
mode = MODE_BETWEEN;
break;
case '\\':
mode = MODE_ESCAPE_DOUBLE;
break;
}
break;
}
}
return output.toString();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java
================================================
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.ParseContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import static com.jayway.jsonpath.internal.Utils.notEmpty;
import static com.jayway.jsonpath.internal.Utils.notNull;
public class ParseContextImpl implements ParseContext {
private final Configuration configuration;
public ParseContextImpl() {
this(Configuration.defaultConfiguration());
}
public ParseContextImpl(Configuration configuration) {
this.configuration = configuration;
}
@Override
public DocumentContext parse(Object json) {
notNull(json, "json object can not be null");
return new JsonContext(json, configuration);
}
@Override
public DocumentContext parse(String json) {
notEmpty(json, "json string can not be null or empty");
Object obj = configuration.jsonProvider().parse(json);
return new JsonContext(obj, configuration);
}
@Override
public DocumentContext parseUtf8(byte[] json) {
notEmpty(json, "json bytes can not be null or empty");
Object obj = configuration.jsonProvider().parse(json);
return new JsonContext(obj, configuration);
}
@Override
public DocumentContext parse(InputStream json) {
return parse(json, "UTF-8");
}
@Override
public DocumentContext parse(InputStream json, String charset) {
notNull(json, "json input stream can not be null");
notNull(charset, "charset can not be null");
try {
Object obj = configuration.jsonProvider().parse(json, charset);
return new JsonContext(obj, configuration);
} finally {
Utils.closeQuietly(json);
}
}
@Override
public DocumentContext parse(File json) throws IOException {
notNull(json, "json file can not be null");
FileInputStream fis = null;
try {
fis = new FileInputStream(json);
return parse(fis);
} finally {
Utils.closeQuietly(fis);
}
}
@Override
@Deprecated
public DocumentContext parse(URL url) throws IOException {
notNull(url, "url can not be null");
InputStream fis = null;
try {
fis = url.openStream();
return parse(fis);
} finally {
Utils.closeQuietly(fis);
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/Path.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
/**
*
*/
public interface Path {
/**
* Evaluates this path
*
* @param document the json document to apply the path on
* @param rootDocument the root json document that started this evaluation
* @param configuration configuration to use
* @return EvaluationContext containing results of evaluation
*/
EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration);
/**
* Evaluates this path
*
* @param document the json document to apply the path on
* @param rootDocument the root json document that started this evaluation
* @param configuration configuration to use
* @param forUpdate is this a read or a write operation
* @return EvaluationContext containing results of evaluation
*/
EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration, boolean forUpdate);
/**
*
* @return true id this path is definite
*/
boolean isDefinite();
/**
*
* @return true id this path is a function
*/
boolean isFunctionPath();
/**
*
* @return true id this path is starts with '$' and false if the path starts with '@'
*/
boolean isRootPath();
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java
================================================
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.InvalidModificationException;
import com.jayway.jsonpath.MapFunction;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.Collection;
public abstract class PathRef implements Comparable {
public static final PathRef NO_OP = new PathRef(null){
@Override
public Object getAccessor() {
return null;
}
@Override
public void set(Object newVal, Configuration configuration) {}
@Override
public void convert(MapFunction mapFunction, Configuration configuration) {}
@Override
public void delete(Configuration configuration) {}
@Override
public void add(Object newVal, Configuration configuration) {}
@Override
public void put(String key, Object newVal, Configuration configuration) {}
@Override
public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {}
};
protected Object parent;
private PathRef(Object parent) {
this.parent = parent;
}
abstract Object getAccessor();
public abstract void set(Object newVal, Configuration configuration);
public abstract void convert(MapFunction mapFunction, Configuration configuration);
public abstract void delete(Configuration configuration);
public abstract void add(Object newVal, Configuration configuration);
public abstract void put(String key, Object newVal, Configuration configuration);
public abstract void renameKey(String oldKey,String newKeyName, Configuration configuration);
protected void renameInMap(Object targetMap, String oldKeyName, String newKeyName, Configuration configuration){
if(configuration.jsonProvider().isMap(targetMap)){
if(configuration.jsonProvider().getMapValue(targetMap, oldKeyName) == JsonProvider.UNDEFINED){
throw new PathNotFoundException("No results for Key "+oldKeyName+" found in map!");
}
configuration.jsonProvider().setProperty(targetMap, newKeyName, configuration.jsonProvider().getMapValue(targetMap, oldKeyName));
configuration.jsonProvider().removeProperty(targetMap, oldKeyName);
} else {
throw new InvalidModificationException("Can only rename properties in a map");
}
}
protected boolean targetInvalid(Object target){
return target == JsonProvider.UNDEFINED || target == null;
}
@Override
public int compareTo(PathRef o) {
return this.getAccessor().toString().compareTo(o.getAccessor().toString()) * -1;
}
public static PathRef create(Object obj, String property){
return new ObjectPropertyPathRef(obj, property);
}
public static PathRef create(Object obj, Collection properties){
return new ObjectMultiPropertyPathRef(obj, properties);
}
public static PathRef create(Object array, int index){
return new ArrayIndexPathRef(array, index);
}
public static PathRef createRoot(Object root){
return new RootPathRef(root);
}
private static class RootPathRef extends PathRef {
private RootPathRef(Object parent) {
super(parent);
}
@Override
Object getAccessor() {
return "$";
}
@Override
public void set(Object newVal, Configuration configuration) {
throw new InvalidModificationException("Invalid set operation");
}
public void convert(MapFunction mapFunction, Configuration configuration){
throw new InvalidModificationException("Invalid map operation");
}
@Override
public void delete(Configuration configuration) {
throw new InvalidModificationException("Invalid delete operation");
}
@Override
public void add(Object newVal, Configuration configuration) {
if(configuration.jsonProvider().isArray(parent)){
configuration.jsonProvider().setArrayIndex(parent, configuration.jsonProvider().length(parent), newVal);
} else {
throw new InvalidModificationException("Invalid add operation. $ is not an array");
}
}
@Override
public void put(String key, Object newVal, Configuration configuration) {
if(configuration.jsonProvider().isMap(parent)){
configuration.jsonProvider().setProperty(parent, key, newVal);
} else {
throw new InvalidModificationException("Invalid put operation. $ is not a map");
}
}
@Override
public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {
Object target = parent;
if(targetInvalid(target)){
return;
}
renameInMap(target, oldKeyName, newKeyName, configuration);
}
}
private static class ArrayIndexPathRef extends PathRef {
private int index;
private ArrayIndexPathRef(Object parent, int index) {
super(parent);
this.index = index;
}
public void set(Object newVal, Configuration configuration){
configuration.jsonProvider().setArrayIndex(parent, index, newVal);
}
public void convert(MapFunction mapFunction, Configuration configuration){
Object currentValue = configuration.jsonProvider().getArrayIndex(parent, index);
configuration.jsonProvider().setArrayIndex(parent, index, mapFunction.map(currentValue, configuration));
}
public void delete(Configuration configuration){
configuration.jsonProvider().removeProperty(parent, index);
}
public void add(Object value, Configuration configuration){
Object target = configuration.jsonProvider().getArrayIndex(parent, index);
if(targetInvalid(target)){
return;
}
if(configuration.jsonProvider().isArray(target)){
configuration.jsonProvider().setProperty(target, null, value);
} else {
throw new InvalidModificationException("Can only add to an array");
}
}
public void put(String key, Object value, Configuration configuration){
Object target = configuration.jsonProvider().getArrayIndex(parent, index);
if(targetInvalid(target)){
return;
}
if(configuration.jsonProvider().isMap(target)){
configuration.jsonProvider().setProperty(target, key, value);
} else {
throw new InvalidModificationException("Can only add properties to a map");
}
}
@Override
public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {
Object target = configuration.jsonProvider().getArrayIndex(parent, index);
if(targetInvalid(target)){
return;
}
renameInMap(target, oldKeyName, newKeyName, configuration);
}
@Override
public Object getAccessor() {
return index;
}
@Override
public int compareTo(PathRef o) {
if(o instanceof ArrayIndexPathRef){
ArrayIndexPathRef pf = (ArrayIndexPathRef) o;
return Integer.compare(pf.index, this.index);
}
return super.compareTo(o);
}
}
private static class ObjectPropertyPathRef extends PathRef {
private String property;
private ObjectPropertyPathRef(Object parent, String property) {
super(parent);
this.property = property;
}
public void set(Object newVal, Configuration configuration){
configuration.jsonProvider().setProperty(parent, property, newVal);
}
@Override
public void convert(MapFunction mapFunction, Configuration configuration) {
Object currentValue = configuration.jsonProvider().getMapValue(parent, property);
configuration.jsonProvider().setProperty(parent, property, mapFunction.map(currentValue, configuration));
}
public void delete(Configuration configuration){
configuration.jsonProvider().removeProperty(parent, property);
}
public void add(Object value, Configuration configuration){
Object target = configuration.jsonProvider().getMapValue(parent, property);
if(targetInvalid(target)){
return;
}
if(configuration.jsonProvider().isArray(target)){
configuration.jsonProvider().setArrayIndex(target, configuration.jsonProvider().length(target), value);
} else {
throw new InvalidModificationException("Can only add to an array");
}
}
public void put(String key, Object value, Configuration configuration){
Object target = configuration.jsonProvider().getMapValue(parent, property);
if(targetInvalid(target)){
return;
}
if(configuration.jsonProvider().isMap(target)){
configuration.jsonProvider().setProperty(target, key, value);
} else {
throw new InvalidModificationException("Can only add properties to a map");
}
}
@Override
public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {
Object target = configuration.jsonProvider().getMapValue(parent, property);
if(targetInvalid(target)){
return;
}
renameInMap(target, oldKeyName, newKeyName, configuration);
}
@Override
public Object getAccessor() {
return property;
}
}
private static class ObjectMultiPropertyPathRef extends PathRef {
private Collection properties;
private ObjectMultiPropertyPathRef(Object parent, Collection properties) {
super(parent);
this.properties = properties;
}
public void set(Object newVal, Configuration configuration){
for (String property : properties) {
configuration.jsonProvider().setProperty(parent, property, newVal);
}
}
public void convert(MapFunction mapFunction, Configuration configuration) {
for (String property : properties) {
Object currentValue = configuration.jsonProvider().getMapValue(parent, property);
if (currentValue != JsonProvider.UNDEFINED) {
configuration.jsonProvider().setProperty(parent, property, mapFunction.map(currentValue, configuration));
}
}
}
public void delete(Configuration configuration){
for (String property : properties) {
configuration.jsonProvider().removeProperty(parent, property);
}
}
@Override
public void add(Object newVal, Configuration configuration) {
throw new InvalidModificationException("Add can not be performed to multiple properties");
}
@Override
public void put(String key, Object newVal, Configuration configuration) {
throw new InvalidModificationException("Put can not be performed to multiple properties");
}
@Override
public void renameKey(String oldKeyName, String newKeyName, Configuration configuration) {
throw new InvalidModificationException("Rename can not be performed to multiple properties");
}
@Override
public Object getAccessor() {
return Utils.join("&&", properties);
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal;
import com.jayway.jsonpath.JsonPathException;
import java.io.Closeable;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Iterator;
public final class Utils {
// accept a collection of objects, since all objects have toString()
public static String join(String delimiter, String wrap, Iterable> objs) {
Iterator> iter = objs.iterator();
if (!iter.hasNext()) {
return "";
}
StringBuilder buffer = new StringBuilder();
buffer.append(wrap).append(iter.next()).append(wrap);
while (iter.hasNext()) {
buffer.append(delimiter).append(wrap).append(iter.next()).append(wrap);
}
return buffer.toString();
}
// accept a collection of objects, since all objects have toString()
public static String join(String delimiter, Iterable> objs) {
return join(delimiter, "", objs);
}
public static String concat(CharSequence... strings) {
if (strings.length == 0) {
return "";
}
if (strings.length == 1) {
return strings[0].toString();
}
int length = 0;
// -1 = no result, -2 = multiple results
int indexOfSingleNonEmptyString = -1;
for (int i = 0; i < strings.length; i++) {
CharSequence charSequence = strings[i];
int len = charSequence.length();
length += len;
if (indexOfSingleNonEmptyString != -2 && len > 0) {
if (indexOfSingleNonEmptyString == -1) {
indexOfSingleNonEmptyString = i;
} else {
indexOfSingleNonEmptyString = -2;
}
}
}
if (length == 0) {
return "";
}
if (indexOfSingleNonEmptyString > 0) {
return strings[indexOfSingleNonEmptyString].toString();
}
StringBuilder sb = new StringBuilder(length);
for (CharSequence charSequence : strings) {
sb.append(charSequence);
}
return sb.toString();
}
//---------------------------------------------------------
//
// IO
//
//---------------------------------------------------------
public static void closeQuietly(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException ignore) {
}
}
public static String escape(String str, boolean escapeSingleQuote) {
if (str == null) {
return null;
}
int len = str.length();
StringWriter writer = new StringWriter(len * 2);
for (int i = 0; i < len; i++) {
char ch = str.charAt(i);
// handle unicode
if (ch > 0xfff) {
writer.write("\\u" + hex(ch));
} else if (ch > 0xff) {
writer.write("\\u0" + hex(ch));
} else if (ch > 0x7f) {
writer.write("\\u00" + hex(ch));
} else if (ch < 32) {
switch (ch) {
case '\b':
writer.write('\\');
writer.write('b');
break;
case '\n':
writer.write('\\');
writer.write('n');
break;
case '\t':
writer.write('\\');
writer.write('t');
break;
case '\f':
writer.write('\\');
writer.write('f');
break;
case '\r':
writer.write('\\');
writer.write('r');
break;
default :
if (ch > 0xf) {
writer.write("\\u00" + hex(ch));
} else {
writer.write("\\u000" + hex(ch));
}
break;
}
} else {
switch (ch) {
case '\'':
if (escapeSingleQuote) {
writer.write('\\');
}
writer.write('\'');
break;
case '"':
writer.write('\\');
writer.write('"');
break;
case '\\':
writer.write('\\');
writer.write('\\');
break;
case '/':
writer.write('\\');
writer.write('/');
break;
default :
writer.write(ch);
break;
}
}
}
return writer.toString();
}
public static String unescape(String str) {
if (str == null) {
return null;
}
int len = str.length();
StringWriter writer = new StringWriter(len);
StringBuilder unicode = new StringBuilder(4);
boolean hadSlash = false;
boolean inUnicode = false;
for (int i = 0; i < len; i++) {
char ch = str.charAt(i);
if (inUnicode) {
unicode.append(ch);
if (unicode.length() == 4) {
try {
int value = Integer.parseInt(unicode.toString(), 16);
writer.write((char) value);
unicode.setLength(0);
inUnicode = false;
hadSlash = false;
} catch (NumberFormatException nfe) {
throw new JsonPathException("Unable to parse unicode value: " + unicode, nfe);
}
}
continue;
}
if (hadSlash) {
hadSlash = false;
switch (ch) {
case '\\':
writer.write('\\');
break;
case '\'':
writer.write('\'');
break;
case '\"':
writer.write('"');
break;
case 'r':
writer.write('\r');
break;
case 'f':
writer.write('\f');
break;
case 't':
writer.write('\t');
break;
case 'n':
writer.write('\n');
break;
case 'b':
writer.write('\b');
break;
case 'u':
{
inUnicode = true;
break;
}
default :
writer.write(ch);
break;
}
continue;
} else if (ch == '\\') {
hadSlash = true;
continue;
}
writer.write(ch);
}
if (hadSlash) {
writer.write('\\');
}
return writer.toString();
}
/**
* Returns an upper case hexadecimal String for the given
* character.
*
* @param ch The character to map.
* @return An upper case hexadecimal String
*/
public static String hex(char ch) {
return Integer.toHexString(ch).toUpperCase();
}
/**
* Checks if a CharSequence is empty ("") or null.
*
*
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
*
*
* NOTE: This method changed in Lang version 2.0.
* It no longer trims the CharSequence.
* That functionality is available in isBlank().
*
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is empty or null
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
*/
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
/**
* Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
*
* @param cs the {@code CharSequence} to be processed
* @param searchChar the {@code CharSequence} to be searched for
* @param start the start index
* @return the index where the search sequence was found
*/
static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
return cs.toString().indexOf(searchChar.toString(), start);
}
//---------------------------------------------------------
//
// Validators
//
//---------------------------------------------------------
/**
* Validate that the specified argument is not {@code null};
* otherwise throwing an exception with the specified message.
*
* Validate.notNull(myObject, "The object must not be null");
*
* @param the object type
* @param object the object to check
* @param message the {@link String#format(String, Object...)} exception message if invalid, not null
* @return the validated object (never {@code null} for method chaining)
* @throws NullPointerException if the object is {@code null}
*/
public static T notNull(T object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
return object;
}
/**
* Validate that the specified argument is not {@code null};
* otherwise throwing an exception with the specified message.
*
* Validate.notNull(myObject, "The object must not be null");
*
* @param the object type
* @param object the object to check
* @param message the {@link String#format(String, Object...)} exception message if invalid, not null
* @param values the optional values for the formatted exception message
* @return the validated object (never {@code null} for method chaining)
* @throws NullPointerException if the object is {@code null}
*/
public static T notNull(T object, String message, Object... values) {
if (object == null) {
throw new IllegalArgumentException(String.format(message, values));
}
return object;
}
/**
* Validate that the argument condition is {@code true}; otherwise
* throwing an exception with the specified message. This method is useful when
* validating according to an arbitrary boolean expression, such as validating a
* primitive number or using your own custom validation expression.
*
* Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);
*
* For performance reasons, the long value is passed as a separate parameter and
* appended to the exception message only in the case of an error.
*
* @param expression the boolean expression to check
* @param message
* @throws IllegalArgumentException if expression is {@code false}
*/
public static void isTrue(boolean expression, String message) {
if (expression == false) {
throw new IllegalArgumentException(message);
}
}
/**
* Check if one and only one condition is true; otherwise
* throw an exception with the specified message.
*
* @param message error describing message
* @param expressions the boolean expressions to check
* @throws IllegalArgumentException if zero or more than one expressions are true
*/
public static void onlyOneIsTrue(final String message, final boolean... expressions) {
if (!onlyOneIsTrueNonThrow(expressions)) {
throw new IllegalArgumentException(message);
}
}
public static boolean onlyOneIsTrueNonThrow(final boolean... expressions) {
int count = 0;
for (final boolean expression : expressions) {
if (expression && ++count > 1) {
return false;
}
}
return 1 == count;
}
/**
* Validate that the specified argument character sequence is
* neither {@code null} nor a length of zero (no characters);
* otherwise throwing an exception with the specified message.
*
* Validate.notEmpty(myString, "The string must not be empty");
*
* @param the character sequence type
* @param chars the character sequence to check, validated not null by this method
* @param message the {@link String#format(String, Object...)} exception message if invalid, not null
* @return the validated character sequence (never {@code null} method for chaining)
* @throws NullPointerException if the character sequence is {@code null}
* @throws IllegalArgumentException if the character sequence is empty
*/
public static T notEmpty(T chars, String message) {
if (chars == null || chars.length() == 0) {
throw new IllegalArgumentException(message);
}
return chars;
}
/**
* Validate that the specified argument character sequence is
* neither {@code null} nor a length of zero (no characters);
* otherwise throwing an exception with the specified message.
*
* Validate.notEmpty(myString, "The string must not be empty");
*
* @param bytes the bytes to check, validated not null by this method
* @param message the {@link String#format(String, Object...)} exception message if invalid, not null
* @return the validated character sequence (never {@code null} method for chaining)
* @throws NullPointerException if the character sequence is {@code null}
* @throws IllegalArgumentException if the character sequence is empty
*/
public static byte[] notEmpty(byte[] bytes, String message) {
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException(message);
}
return bytes;
}
/**
* Validate that the specified argument character sequence is
* neither {@code null} nor a length of zero (no characters);
* otherwise throwing an exception with the specified message.
*
* Validate.notEmpty(myString, "The string must not be empty");
*
* @param the character sequence type
* @param chars the character sequence to check, validated not null by this method
* @param message the {@link String#format(String, Object...)} exception message if invalid, not null
* @param values the optional values for the formatted exception message, null array not recommended
* @return the validated character sequence (never {@code null} method for chaining)
* @throws NullPointerException if the character sequence is {@code null}
* @throws IllegalArgumentException if the character sequence is empty
*/
public static T notEmpty(T chars, String message, Object... values) {
if (chars == null || chars.length() == 0) {
throw new IllegalArgumentException(String.format(message, values));
}
return chars;
}
//---------------------------------------------------------
//
// Converters
//
//---------------------------------------------------------
public static String toString(Object o) {
if (null == o) {
return null;
}
return o.toString();
}
private Utils() {
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/Evaluator.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.Predicate;
public interface Evaluator {
boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Predicate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.filter.ValueNodes.PatternNode;
import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode;
public class EvaluatorFactory {
private static final Map evaluators = new HashMap();
static {
evaluators.put(RelationalOperator.EXISTS, new ExistsEvaluator());
evaluators.put(RelationalOperator.NE, new NotEqualsEvaluator());
evaluators.put(RelationalOperator.TSNE, new TypeSafeNotEqualsEvaluator());
evaluators.put(RelationalOperator.EQ, new EqualsEvaluator());
evaluators.put(RelationalOperator.TSEQ, new TypeSafeEqualsEvaluator());
evaluators.put(RelationalOperator.LT, new LessThanEvaluator());
evaluators.put(RelationalOperator.LTE, new LessThanEqualsEvaluator());
evaluators.put(RelationalOperator.GT, new GreaterThanEvaluator());
evaluators.put(RelationalOperator.GTE, new GreaterThanEqualsEvaluator());
evaluators.put(RelationalOperator.REGEX, new RegexpEvaluator());
evaluators.put(RelationalOperator.SIZE, new SizeEvaluator());
evaluators.put(RelationalOperator.EMPTY, new EmptyEvaluator());
evaluators.put(RelationalOperator.IN, new InEvaluator());
evaluators.put(RelationalOperator.NIN, new NotInEvaluator());
evaluators.put(RelationalOperator.ALL, new AllEvaluator());
evaluators.put(RelationalOperator.CONTAINS, new ContainsEvaluator());
evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator());
evaluators.put(RelationalOperator.TYPE, new TypeEvaluator());
evaluators.put(RelationalOperator.SUBSETOF, new SubsetOfEvaluator());
evaluators.put(RelationalOperator.ANYOF, new AnyOfEvaluator());
evaluators.put(RelationalOperator.NONEOF, new NoneOfEvaluator());
}
public static Evaluator createEvaluator(RelationalOperator operator){
return evaluators.get(operator);
}
private static class ExistsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(!left.isBooleanNode() && !right.isBooleanNode()){
throw new JsonPathException("Failed to evaluate exists expression");
}
return left.asBooleanNode().getBoolean() == right.asBooleanNode().getBoolean();
}
}
private static class NotEqualsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
return !evaluators.get(RelationalOperator.EQ).evaluate(left, right, ctx);
}
}
private static class TypeSafeNotEqualsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
return !evaluators.get(RelationalOperator.TSEQ).evaluate(left, right, ctx);
}
}
private static class EqualsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isJsonNode() && right.isJsonNode()){
return left.asJsonNode().equals(right.asJsonNode(), ctx);
} else {
return left.equals(right);
}
}
}
private static class TypeSafeEqualsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(!left.getClass().equals(right.getClass())){
return false;
}
return evaluators.get(RelationalOperator.EQ).evaluate(left, right, ctx);
}
}
private static class TypeEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
return right.asClassNode().getClazz() == left.type(ctx);
}
}
private static class LessThanEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isNumberNode() && right.isNumberNode()){
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) < 0;
} if(left.isStringNode() && right.isStringNode()){
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) < 0;
} if (left.isOffsetDateTimeNode() && right.isOffsetDateTimeNode()){ //workaround for issue: https://github.com/json-path/JsonPath/issues/613
return left.asOffsetDateTimeNode().getDate().compareTo(right.asOffsetDateTimeNode().getDate()) < 0;
}
return false;
}
}
private static class LessThanEqualsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isNumberNode() && right.isNumberNode()){
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) <= 0;
} if(left.isStringNode() && right.isStringNode()){
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) <= 0;
} if (left.isOffsetDateTimeNode() && right.isOffsetDateTimeNode()){ //workaround for issue: https://github.com/json-path/JsonPath/issues/613
return left.asOffsetDateTimeNode().getDate().compareTo(right.asOffsetDateTimeNode().getDate()) <= 0;
}
return false;
}
}
private static class GreaterThanEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isNumberNode() && right.isNumberNode()){
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) > 0;
} else if(left.isStringNode() && right.isStringNode()){
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) > 0;
} else if (left.isOffsetDateTimeNode() && right.isOffsetDateTimeNode()){ //workaround for issue: https://github.com/json-path/JsonPath/issues/613
return left.asOffsetDateTimeNode().getDate().compareTo(right.asOffsetDateTimeNode().getDate()) > 0;
}
return false;
}
}
private static class GreaterThanEqualsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isNumberNode() && right.isNumberNode()){
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) >= 0;
} else if(left.isStringNode() && right.isStringNode()){
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) >= 0;
} else if (left.isOffsetDateTimeNode() && right.isOffsetDateTimeNode()){ //workaround for issue: https://github.com/json-path/JsonPath/issues/613
return left.asOffsetDateTimeNode().getDate().compareTo(right.asOffsetDateTimeNode().getDate()) >= 0;
}
return false;
}
}
private static class SizeEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if (! right.isNumberNode()) {
return false;
}
int expectedSize = right.asNumberNode().getNumber().intValue();
if(left.isStringNode()){
return left.asStringNode().length() == expectedSize;
} else if(left.isJsonNode()){
return left.asJsonNode().length(ctx) == expectedSize;
}
return false;
}
}
private static class EmptyEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isStringNode()){
return left.asStringNode().isEmpty() == right.asBooleanNode().getBoolean();
} else if(left.isJsonNode()){
return left.asJsonNode().isEmpty(ctx) == right.asBooleanNode().getBoolean();
}
return false;
}
}
private static class InEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueListNode valueListNode;
if(right.isJsonNode()){
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
return false;
} else {
valueListNode = vn.asValueListNode();
}
} else {
valueListNode = right.asValueListNode();
}
return valueListNode.contains(left);
}
}
private static class NotInEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
return !evaluators.get(RelationalOperator.IN).evaluate(left, right, ctx);
}
}
private static class AllEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueListNode requiredValues = right.asValueListNode();
if(left.isJsonNode()){
ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); //returns UndefinedNode if conversion is not possible
if(valueNode.isValueListNode()){
ValueListNode shouldContainAll = valueNode.asValueListNode();
for (ValueNode required : requiredValues) {
if(!shouldContainAll.contains(required)){
return false;
}
}
}
return true;
}
return false;
}
}
private static class ContainsEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(left.isStringNode() && right.isStringNode()){
return left.asStringNode().contains(right.asStringNode().getString());
} else if(left.isJsonNode()){
ValueNode valueNode = left.asJsonNode().asValueListNode(ctx);
if(valueNode.isUndefinedNode()) return false;
else {
boolean res = valueNode.asValueListNode().contains(right);
return res;
}
}
return false;
}
}
private static class PredicateMatchEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
return right.asPredicateNode().getPredicate().apply(ctx);
}
}
private static class RegexpEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
if(!(left.isPatternNode() ^ right.isPatternNode())){
return false;
}
if (left.isPatternNode()) {
if (right.isValueListNode() || (right.isJsonNode() && right.asJsonNode().isArray(ctx))) {
return matchesAny(left.asPatternNode(), right.asJsonNode().asValueListNode(ctx));
} else {
return matches(left.asPatternNode(), getInput(right));
}
} else {
if (left.isValueListNode() || (left.isJsonNode() && left.asJsonNode().isArray(ctx))) {
return matchesAny(right.asPatternNode(), left.asJsonNode().asValueListNode(ctx));
} else {
return matches(right.asPatternNode(), getInput(left));
}
}
}
private boolean matches(PatternNode patternNode, String inputToMatch) {
return patternNode.getCompiledPattern().matcher(inputToMatch).matches();
}
private boolean matchesAny(PatternNode patternNode, ValueNode valueNode) {
if (!valueNode.isValueListNode()) {
return false;
}
ValueListNode listNode = valueNode.asValueListNode();
Pattern pattern = patternNode.getCompiledPattern();
for (Iterator it = listNode.iterator(); it.hasNext(); ) {
String input = getInput(it.next());
if (pattern.matcher(input).matches()) {
return true;
}
}
return false;
}
private String getInput(ValueNode valueNode) {
String input = "";
if (valueNode.isStringNode() || valueNode.isNumberNode()) {
input = valueNode.asStringNode().getString();
} else if (valueNode.isBooleanNode()) {
input = valueNode.asBooleanNode().toString();
}
return input;
}
}
private static class SubsetOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueListNode rightValueListNode;
if(right.isJsonNode()){
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
return false;
} else {
rightValueListNode = vn.asValueListNode();
}
} else {
rightValueListNode = right.asValueListNode();
}
ValueListNode leftValueListNode;
if(left.isJsonNode()){
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if(vn.isUndefinedNode()){
return false;
} else {
leftValueListNode = vn.asValueListNode();
}
} else {
leftValueListNode = left.asValueListNode();
}
return leftValueListNode.subsetof(rightValueListNode);
}
}
private static class AnyOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueListNode rightValueListNode;
if (right.isJsonNode()) {
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
rightValueListNode = vn.asValueListNode();
}
} else {
rightValueListNode = right.asValueListNode();
}
ValueListNode leftValueListNode;
if (left.isJsonNode()) {
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
leftValueListNode = vn.asValueListNode();
}
} else {
leftValueListNode = left.asValueListNode();
}
for (ValueNode leftValueNode : leftValueListNode) {
for (ValueNode rightValueNode : rightValueListNode) {
if (leftValueNode.equals(rightValueNode)) {
return true;
}
}
}
return false;
}
}
private static class NoneOfEvaluator implements Evaluator {
@Override
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) {
ValueListNode rightValueListNode;
if (right.isJsonNode()) {
ValueNode vn = right.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
rightValueListNode = vn.asValueListNode();
}
} else {
rightValueListNode = right.asValueListNode();
}
ValueListNode leftValueListNode;
if (left.isJsonNode()) {
ValueNode vn = left.asJsonNode().asValueListNode(ctx);
if (vn.isUndefinedNode()) {
return false;
} else {
leftValueListNode = vn.asValueListNode();
}
} else {
leftValueListNode = left.asValueListNode();
}
for (ValueNode leftValueNode : leftValueListNode) {
for (ValueNode rightValueNode : rightValueListNode) {
if (leftValueNode.equals(rightValueNode)) {
return false;
}
}
}
return true;
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/ExpressionNode.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.Predicate;
public abstract class ExpressionNode implements Predicate {
public static ExpressionNode createExpressionNode(ExpressionNode right, LogicalOperator operator, ExpressionNode left){
if(operator == LogicalOperator.AND){
if((right instanceof LogicalExpressionNode) && ((LogicalExpressionNode)right).getOperator() == LogicalOperator.AND ){
LogicalExpressionNode len = (LogicalExpressionNode) right;
return len.append(left);
} else {
return LogicalExpressionNode.createLogicalAnd(left, right);
}
} else {
if((right instanceof LogicalExpressionNode) && ((LogicalExpressionNode)right).getOperator() == LogicalOperator.OR ){
LogicalExpressionNode len = (LogicalExpressionNode) right;
return len.append(left);
} else {
return LogicalExpressionNode.createLogicalOr(left, right);
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.CharacterIndex;
import static com.jayway.jsonpath.internal.filter.ValueNodes.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class FilterCompiler {
private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class);
private static final char DOC_CONTEXT = '$';
private static final char EVAL_CONTEXT = '@';
private static final char OPEN_SQUARE_BRACKET = '[';
private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_PARENTHESIS = ')';
private static final char OPEN_OBJECT = '{';
private static final char CLOSE_OBJECT = '}';
private static final char OPEN_ARRAY = '[';
private static final char CLOSE_ARRAY = ']';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final char SPACE = ' ';
private static final char PERIOD = '.';
private static final char AND = '&';
private static final char OR = '|';
private static final char MINUS = '-';
private static final char LT = '<';
private static final char GT = '>';
private static final char EQ = '=';
private static final char TILDE = '~';
private static final char TRUE = 't';
private static final char FALSE = 'f';
private static final char NULL = 'n';
private static final char NOT = '!';
private static final char PATTERN = '/';
private static final char IGNORE_CASE = 'i';
private CharacterIndex filter;
public static Filter compile(String filterString) {
FilterCompiler compiler = new FilterCompiler(filterString);
return new CompiledFilter(compiler.compile());
}
private FilterCompiler(String filterString) {
filter = new CharacterIndex(filterString);
filter.trim();
if (!filter.currentCharIs('[') || !filter.lastCharIs(']')) {
throw new InvalidPathException("Filter must start with '[' and end with ']'. " + filterString);
}
filter.incrementPosition(1);
filter.decrementEndPosition(1);
filter.trim();
if (!filter.currentCharIs('?')) {
throw new InvalidPathException("Filter must start with '[?' and end with ']'. " + filterString);
}
filter.incrementPosition(1);
filter.trim();
if (!filter.currentCharIs('(') || !filter.lastCharIs(')')) {
throw new InvalidPathException("Filter must start with '[?(' and end with ')]'. " + filterString);
}
}
public Predicate compile() {
try {
final ExpressionNode result = readLogicalOR();
filter.skipBlanks();
if (filter.inBounds()) {
throw new InvalidPathException(String.format("Expected end of filter expression instead of: %s",
filter.subSequence(filter.position(), filter.length())));
}
return result;
} catch (InvalidPathException e){
throw e;
} catch (Exception e) {
throw new InvalidPathException("Failed to parse filter: " + filter + ", error on position: " + filter.position() + ", char: " + filter.currentChar());
}
}
private ValueNode readValueNode() {
switch (filter.skipBlanks().currentChar()) {
case DOC_CONTEXT : return readPath();
case EVAL_CONTEXT : return readPath();
case NOT:
filter.incrementPosition(1);
switch (filter.skipBlanks().currentChar()) {
case DOC_CONTEXT : return readPath();
case EVAL_CONTEXT : return readPath();
default: throw new InvalidPathException(String.format("Unexpected character: %c", NOT));
}
default : return readLiteral();
}
}
private ValueNode readLiteral(){
switch (filter.skipBlanks().currentChar()){
case SINGLE_QUOTE: return readStringLiteral(SINGLE_QUOTE);
case DOUBLE_QUOTE: return readStringLiteral(DOUBLE_QUOTE);
case TRUE: return readBooleanLiteral();
case FALSE: return readBooleanLiteral();
case MINUS: return readNumberLiteral();
case NULL: return readNullLiteral();
case OPEN_OBJECT: return readJsonLiteral();
case OPEN_ARRAY: return readJsonLiteral();
case PATTERN: return readPattern();
default: return readNumberLiteral();
}
}
/*
* LogicalOR = LogicalAND { '||' LogicalAND }
* LogicalAND = LogicalANDOperand { '&&' LogicalANDOperand }
* LogicalANDOperand = RelationalExpression | '(' LogicalOR ')' | '!' LogicalANDOperand
* RelationalExpression = Value [ RelationalOperator Value ]
*/
private ExpressionNode readLogicalOR() {
final List ops = new ArrayList();
ops.add(readLogicalAND());
while (true) {
int savepoint = filter.position();
if (filter.hasSignificantSubSequence(LogicalOperator.OR.getOperatorString())) {
ops.add(readLogicalAND());
} else {
filter.setPosition(savepoint);
break;
}
}
return 1 == ops.size() ? ops.get(0) : LogicalExpressionNode.createLogicalOr(ops);
}
private ExpressionNode readLogicalAND() {
/// @fixme copy-pasted
final List ops = new ArrayList();
ops.add(readLogicalANDOperand());
while (true) {
int savepoint = filter.position();
if (filter.hasSignificantSubSequence(LogicalOperator.AND.getOperatorString())) {
ops.add(readLogicalANDOperand());
} else {
filter.setPosition(savepoint);
break;
}
}
return 1 == ops.size() ? ops.get(0) : LogicalExpressionNode.createLogicalAnd(ops);
}
private ExpressionNode readLogicalANDOperand() {
int savepoint = filter.skipBlanks().position();
if (filter.skipBlanks().currentCharIs(NOT)) {
filter.readSignificantChar(NOT);
switch (filter.skipBlanks().currentChar()) {
case DOC_CONTEXT:
case EVAL_CONTEXT:
filter.setPosition(savepoint);
break;
default:
final ExpressionNode op = readLogicalANDOperand();
return LogicalExpressionNode.createLogicalNot(op);
}
}
if (filter.skipBlanks().currentCharIs(OPEN_PARENTHESIS)) {
filter.readSignificantChar(OPEN_PARENTHESIS);
final ExpressionNode op = readLogicalOR();
filter.readSignificantChar(CLOSE_PARENTHESIS);
return op;
}
return readExpression();
}
private RelationalExpressionNode readExpression() {
ValueNode left = readValueNode();
int savepoint = filter.position();
try {
RelationalOperator operator = readRelationalOperator();
ValueNode right = readValueNode();
return new RelationalExpressionNode(left, operator, right);
}
catch (InvalidPathException exc) {
filter.setPosition(savepoint);
}
PathNode pathNode = left.asPathNode();
left = pathNode.asExistsCheck(pathNode.shouldExists());
RelationalOperator operator = RelationalOperator.EXISTS;
ValueNode right = left.asPathNode().shouldExists() ? ValueNodes.TRUE : ValueNodes.FALSE;
return new RelationalExpressionNode(left, operator, right);
}
private LogicalOperator readLogicalOperator(){
int begin = filter.skipBlanks().position();
int end = begin+1;
if(!filter.inBounds(end)){
throw new InvalidPathException("Expected boolean literal");
}
CharSequence logicalOperator = filter.subSequence(begin, end+1);
if(!logicalOperator.equals("||") && !logicalOperator.equals("&&")){
throw new InvalidPathException("Expected logical operator");
}
filter.incrementPosition(logicalOperator.length());
logger.trace("LogicalOperator from {} to {} -> [{}]", begin, end, logicalOperator);
return LogicalOperator.fromString(logicalOperator.toString());
}
private RelationalOperator readRelationalOperator() {
int begin = filter.skipBlanks().position();
if(isRelationalOperatorChar(filter.currentChar())){
while (filter.inBounds() && isRelationalOperatorChar(filter.currentChar())) {
filter.incrementPosition(1);
}
} else {
while (filter.inBounds() && filter.currentChar() != SPACE) {
filter.incrementPosition(1);
}
}
CharSequence operator = filter.subSequence(begin, filter.position());
logger.trace("Operator from {} to {} -> [{}]", begin, filter.position()-1, operator);
return RelationalOperator.fromString(operator.toString());
}
private NullNode readNullLiteral() {
int begin = filter.position();
if(filter.currentChar() == NULL && filter.inBounds(filter.position() + 3)){
CharSequence nullValue = filter.subSequence(filter.position(), filter.position() + 4);
if("null".equals(nullValue.toString())){
logger.trace("NullLiteral from {} to {} -> [{}]", begin, filter.position()+3, nullValue);
filter.incrementPosition(nullValue.length());
return ValueNode.createNullNode();
}
}
throw new InvalidPathException("Expected value");
}
private JsonNode readJsonLiteral(){
int begin = filter.position();
char openChar = filter.currentChar();
assert openChar == OPEN_ARRAY || openChar == OPEN_OBJECT;
char closeChar = openChar == OPEN_ARRAY ? CLOSE_ARRAY : CLOSE_OBJECT;
int closingIndex = filter.indexOfMatchingCloseChar(filter.position(), openChar, closeChar, true, false);
if (closingIndex == -1) {
throw new InvalidPathException("String not closed. Expected " + SINGLE_QUOTE + " in " + filter);
} else {
filter.setPosition(closingIndex + 1);
}
CharSequence json = filter.subSequence(begin, filter.position());
logger.trace("JsonLiteral from {} to {} -> [{}]", begin, filter.position(), json);
return ValueNode.createJsonNode(json);
}
private int endOfFlags(int position) {
int endIndex = position;
char[] currentChar = new char[1];
while (filter.inBounds(endIndex)) {
currentChar[0] = filter.charAt(endIndex);
if (PatternFlag.parseFlags(currentChar) > 0) {
endIndex++;
continue;
}
break;
}
return endIndex;
}
private PatternNode readPattern() {
int begin = filter.position();
int closingIndex = filter.nextIndexOfUnescaped(PATTERN);
if (closingIndex == -1) {
throw new InvalidPathException("Pattern not closed. Expected " + PATTERN + " in " + filter);
} else {
if (filter.inBounds(closingIndex+1)) {
int endFlagsIndex = endOfFlags(closingIndex + 1);
if (endFlagsIndex > closingIndex) {
CharSequence flags = filter.subSequence(closingIndex + 1, endFlagsIndex);
closingIndex += flags.length();
}
}
filter.setPosition(closingIndex + 1);
}
CharSequence pattern = filter.subSequence(begin, filter.position());
logger.trace("PatternNode from {} to {} -> [{}]", begin, filter.position(), pattern);
return ValueNode.createPatternNode(pattern);
}
private StringNode readStringLiteral(char endChar) {
int begin = filter.position();
int closingSingleQuoteIndex = filter.nextIndexOfUnescaped(endChar);
if (closingSingleQuoteIndex == -1) {
throw new InvalidPathException("String literal does not have matching quotes. Expected " + endChar + " in " + filter);
} else {
filter.setPosition(closingSingleQuoteIndex + 1);
}
CharSequence stringLiteral = filter.subSequence(begin, filter.position());
logger.trace("StringLiteral from {} to {} -> [{}]", begin, filter.position(), stringLiteral);
return ValueNode.createStringNode(stringLiteral, true);
}
private NumberNode readNumberLiteral() {
int begin = filter.position();
while (filter.inBounds() && filter.isNumberCharacter(filter.position())) {
filter.incrementPosition(1);
}
CharSequence numberLiteral = filter.subSequence(begin, filter.position());
logger.trace("NumberLiteral from {} to {} -> [{}]", begin, filter.position(), numberLiteral);
return ValueNode.createNumberNode(numberLiteral);
}
private BooleanNode readBooleanLiteral() {
int begin = filter.position();
int end = filter.currentChar() == TRUE ? filter.position() + 3 : filter.position() + 4;
if(!filter.inBounds(end)){
throw new InvalidPathException("Expected boolean literal");
}
CharSequence boolValue = filter.subSequence(begin, end+1);
if(!boolValue.equals("true") && !boolValue.equals("false")){
throw new InvalidPathException("Expected boolean literal");
}
filter.incrementPosition(boolValue.length());
logger.trace("BooleanLiteral from {} to {} -> [{}]", begin, end, boolValue);
return ValueNode.createBooleanNode(boolValue);
}
private PathNode readPath() {
char previousSignificantChar = filter.previousSignificantChar();
int begin = filter.position();
filter.incrementPosition(1); //skip $ and @
while (filter.inBounds()) {
if (filter.currentChar() == OPEN_SQUARE_BRACKET) {
int closingSquareBracketIndex = filter.indexOfMatchingCloseChar(filter.position(), OPEN_SQUARE_BRACKET, CLOSE_SQUARE_BRACKET, true, false);
if (closingSquareBracketIndex == -1) {
throw new InvalidPathException("Square brackets does not match in filter " + filter);
} else {
filter.setPosition(closingSquareBracketIndex + 1);
}
}
boolean closingFunctionBracket = (filter.currentChar() == CLOSE_PARENTHESIS && currentCharIsClosingFunctionBracket(begin));
boolean closingLogicalBracket = (filter.currentChar() == CLOSE_PARENTHESIS && !closingFunctionBracket);
if (!filter.inBounds() || isRelationalOperatorChar(filter.currentChar()) || filter.currentChar() == SPACE || closingLogicalBracket) {
break;
} else {
filter.incrementPosition(1);
}
}
boolean shouldExists = !(previousSignificantChar == NOT);
CharSequence path = filter.subSequence(begin, filter.position());
return ValueNode.createPathNode(path, false, shouldExists);
}
private boolean expressionIsTerminated(){
char c = filter.currentChar();
if(c == CLOSE_PARENTHESIS || isLogicalOperatorChar(c)){
return true;
}
c = filter.nextSignificantChar();
if(c == CLOSE_PARENTHESIS || isLogicalOperatorChar(c)){
return true;
}
return false;
}
private boolean currentCharIsClosingFunctionBracket(int lowerBound){
if(filter.currentChar() != CLOSE_PARENTHESIS){
return false;
}
int idx = filter.indexOfPreviousSignificantChar();
if(idx == -1 || filter.charAt(idx) != OPEN_PARENTHESIS){
return false;
}
idx--;
while(filter.inBounds(idx) && idx > lowerBound){
if(filter.charAt(idx) == PERIOD){
return true;
}
idx--;
}
return false;
}
private boolean isLogicalOperatorChar(char c) {
return c == AND || c == OR;
}
private boolean isRelationalOperatorChar(char c) {
return c == LT || c == GT || c == EQ || c == TILDE || c == NOT;
}
private static final class CompiledFilter extends Filter {
private final Predicate predicate;
private CompiledFilter(Predicate predicate) {
this.predicate = predicate;
}
@Override
public boolean apply(Predicate.PredicateContext ctx) {
return predicate.apply(ctx);
}
@Override
public String toString() {
String predicateString = predicate.toString();
if(predicateString.startsWith("(")){
return "[?" + predicateString + "]";
} else {
return "[?(" + predicateString + ")]";
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalExpressionNode.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.internal.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class LogicalExpressionNode extends ExpressionNode {
protected List chain = new ArrayList();
private final LogicalOperator operator;
public static ExpressionNode createLogicalNot(ExpressionNode op) {
return new LogicalExpressionNode(op, LogicalOperator.NOT, null);
}
public static LogicalExpressionNode createLogicalOr(ExpressionNode left,ExpressionNode right){
return new LogicalExpressionNode(left, LogicalOperator.OR, right);
}
public static LogicalExpressionNode createLogicalOr(Collection operands){
return new LogicalExpressionNode(LogicalOperator.OR, operands);
}
public static LogicalExpressionNode createLogicalAnd(ExpressionNode left,ExpressionNode right){
return new LogicalExpressionNode(left, LogicalOperator.AND, right);
}
public static LogicalExpressionNode createLogicalAnd(Collection operands){
return new LogicalExpressionNode(LogicalOperator.AND, operands);
}
private LogicalExpressionNode(ExpressionNode left, LogicalOperator operator, ExpressionNode right) {
chain.add(left);
chain.add(right);
this.operator = operator;
}
private LogicalExpressionNode(LogicalOperator operator, Collection operands) {
chain.addAll(operands);
this.operator = operator;
}
public LogicalExpressionNode and(LogicalExpressionNode other){
return createLogicalAnd(this, other);
}
public LogicalExpressionNode or(LogicalExpressionNode other){
return createLogicalOr(this, other);
}
public LogicalOperator getOperator() {
return operator;
}
public LogicalExpressionNode append(ExpressionNode expressionNode) {
chain.add(0, expressionNode);
return this;
}
@Override
public String toString() {
return "(" + Utils.join(" " + operator.getOperatorString() + " ", chain) + ")";
}
@Override
public boolean apply(PredicateContext ctx) {
if(operator == LogicalOperator.OR){
for (ExpressionNode expression : chain) {
if(expression.apply(ctx)){
return true;
}
}
return false;
} else if (operator == LogicalOperator.AND) {
for (ExpressionNode expression : chain) {
if(!expression.apply(ctx)){
return false;
}
}
return true;
} else {
ExpressionNode expression = chain.get(0);
return !expression.apply(ctx);
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/LogicalOperator.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.InvalidPathException;
public enum LogicalOperator {
AND("&&"),
NOT("!"),
OR("||");
private final String operatorString;
LogicalOperator(String operatorString) {
this.operatorString = operatorString;
}
public String getOperatorString() {
return operatorString;
}
@Override
public String toString() {
return operatorString;
}
public static LogicalOperator fromString(String operatorString){
if(AND.operatorString.equals(operatorString)) return AND;
else if(NOT.operatorString.equals(operatorString)) return NOT;
else if(OR.operatorString.equals(operatorString)) return OR;
else throw new InvalidPathException("Failed to parse operator " + operatorString);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java
================================================
package com.jayway.jsonpath.internal.filter;
import java.util.regex.Pattern;
public enum PatternFlag {
UNIX_LINES(Pattern.UNIX_LINES, 'd'),
CASE_INSENSITIVE(Pattern.CASE_INSENSITIVE, 'i'),
COMMENTS(Pattern.COMMENTS, 'x'),
MULTILINE(Pattern.MULTILINE, 'm'),
DOTALL(Pattern.DOTALL, 's'),
UNICODE_CASE(Pattern.UNICODE_CASE, 'u'),
UNICODE_CHARACTER_CLASS(Pattern.UNICODE_CHARACTER_CLASS, 'U');
private final int code;
private final char flag;
private PatternFlag(int code, char flag) {
this.code = code;
this.flag = flag;
}
public static int parseFlags(char[] flags) {
int flagsValue = 0;
for (char flag : flags) {
flagsValue |= getCodeByFlag(flag);
}
return flagsValue;
}
public static String parseFlags(int flags) {
StringBuilder builder = new StringBuilder();
for (PatternFlag patternFlag : PatternFlag.values()) {
if ((patternFlag.code & flags) == patternFlag.code) {
builder.append(patternFlag.flag);
}
}
return builder.toString();
}
private static int getCodeByFlag(char flag) {
for (PatternFlag patternFlag : PatternFlag.values()) {
if (patternFlag.flag == flag) {
return patternFlag.code;
}
}
return 0;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalExpressionNode.java
================================================
package com.jayway.jsonpath.internal.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RelationalExpressionNode extends ExpressionNode {
private static final Logger logger = LoggerFactory.getLogger(RelationalExpressionNode.class);
private final ValueNode left;
private final RelationalOperator relationalOperator;
private final ValueNode right;
public RelationalExpressionNode(ValueNode left, RelationalOperator relationalOperator, ValueNode right) {
this.left = left;
this.relationalOperator = relationalOperator;
this.right = right;
logger.trace("ExpressionNode {}", toString());
}
@Override
public String toString() {
if(relationalOperator == RelationalOperator.EXISTS){
return left.toString();
} else {
return left.toString() + " " + relationalOperator.toString() + " " + right.toString();
}
}
@Override
public boolean apply(PredicateContext ctx) {
ValueNode l = left;
ValueNode r = right;
if(left.isPathNode()){
l = left.asPathNode().evaluate(ctx);
}
if(right.isPathNode()){
r = right.asPathNode().evaluate(ctx);
}
Evaluator evaluator = EvaluatorFactory.createEvaluator(relationalOperator);
if(evaluator != null){
return evaluator.evaluate(l, r, ctx);
}
return false;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.InvalidPathException;
import java.util.Locale;
public enum RelationalOperator {
GTE(">="),
LTE("<="),
EQ("=="),
/**
* Type safe equals
*/
TSEQ("==="),
NE("!="),
/**
* Type safe not equals
*/
TSNE("!=="),
LT("<"),
GT(">"),
REGEX("=~"),
NIN("NIN"),
IN("IN"),
CONTAINS("CONTAINS"),
ALL("ALL"),
SIZE("SIZE"),
EXISTS("EXISTS"),
TYPE("TYPE"),
MATCHES("MATCHES"),
EMPTY("EMPTY"),
SUBSETOF("SUBSETOF"),
ANYOF("ANYOF"),
NONEOF("NONEOF");
private final String operatorString;
RelationalOperator(String operatorString) {
this.operatorString = operatorString;
}
public static RelationalOperator fromString(String operatorString) {
String upperCaseOperatorString = operatorString.toUpperCase(Locale.ROOT);
for (RelationalOperator operator : RelationalOperator.values()) {
if(operator.operatorString.equals(upperCaseOperatorString) ){
return operator;
}
}
throw new InvalidPathException("Filter operator " + operatorString + " is not supported!");
}
@Override
public String toString() {
return operatorString;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java
================================================
package com.jayway.jsonpath.internal.filter;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.path.PathCompiler;
import net.minidev.json.parser.JSONParser;
import java.time.OffsetDateTime;
import java.util.regex.Pattern;
import static com.jayway.jsonpath.internal.filter.ValueNodes.*;
public abstract class ValueNode {
public abstract Class> type(Predicate.PredicateContext ctx);
public boolean isPatternNode() {
return false;
}
public PatternNode asPatternNode() {
throw new InvalidPathException("Expected regexp node");
}
public boolean isPathNode() {
return false;
}
public PathNode asPathNode() {
throw new InvalidPathException("Expected path node");
}
public boolean isNumberNode() {
return false;
}
public NumberNode asNumberNode() {
throw new InvalidPathException("Expected number node");
}
public boolean isStringNode() {
return false;
}
public StringNode asStringNode() {
throw new InvalidPathException("Expected string node");
}
public boolean isBooleanNode() {
return false;
}
public BooleanNode asBooleanNode() {
throw new InvalidPathException("Expected boolean node");
}
public boolean isJsonNode() {
return false;
}
public JsonNode asJsonNode() {
throw new InvalidPathException("Expected json node");
}
public boolean isPredicateNode() {
return false;
}
public PredicateNode asPredicateNode() {
throw new InvalidPathException("Expected predicate node");
}
public boolean isValueListNode() {
return false;
}
public ValueListNode asValueListNode() {
throw new InvalidPathException("Expected value list node");
}
public boolean isNullNode() {
return false;
}
public NullNode asNullNode() {
throw new InvalidPathException("Expected null node");
}
public UndefinedNode asUndefinedNode() {
throw new InvalidPathException("Expected undefined node");
}
public boolean isUndefinedNode() {
return false;
}
public boolean isClassNode() {
return false;
}
public ClassNode asClassNode() {
throw new InvalidPathException("Expected class node");
}
//workaround for issue: https://github.com/json-path/JsonPath/issues/613
public boolean isOffsetDateTimeNode(){
return false;
}
public OffsetDateTimeNode asOffsetDateTimeNode(){
throw new InvalidPathException("Expected offsetDateTime node");
}
private static boolean isPath(Object o) {
if(o == null || !(o instanceof String)){
return false;
}
String str = o.toString().trim();
if (str.length() <= 0) {
return false;
}
char c0 = str.charAt(0);
if(c0 == '@' || c0 == '$'){
try {
PathCompiler.compile(str);
return true;
} catch(Exception e){
return false;
}
}
return false;
}
private static boolean isJson(Object o) {
if(o == null || !(o instanceof String)){
return false;
}
String str = o.toString().trim();
if (str.length() <= 1) {
return false;
}
char c0 = str.charAt(0);
char c1 = str.charAt(str.length() - 1);
if ((c0 == '[' && c1 == ']') || (c0 == '{' && c1 == '}')){
try {
new JSONParser(JSONParser.MODE_PERMISSIVE).parse(str);
return true;
} catch(Exception e){
return false;
}
}
return false;
}
//----------------------------------------------------
//
// Factory methods
//
//----------------------------------------------------
public static ValueNode toValueNode(Object o){
if(o == null) return NULL_NODE;
if(o instanceof ValueNode) return (ValueNode)o;
if(o instanceof Class) return createClassNode((Class)o);
else if(isPath(o)) return new PathNode(o.toString(), false, false);
else if(isJson(o)) return createJsonNode(o.toString());
else if(o instanceof String) return createStringNode(o.toString(), true);
else if(o instanceof Character) return createStringNode(o.toString(), false);
else if(o instanceof Number) return createNumberNode(o.toString());
else if(o instanceof Boolean) return createBooleanNode(o.toString());
else if(o instanceof Pattern) return createPatternNode((Pattern)o);
else if (o instanceof OffsetDateTime) return createOffsetDateTimeNode(o.toString()); //workaround for issue: https://github.com/json-path/JsonPath/issues/613
else throw new JsonPathException("Could not determine value type");
}
public static StringNode createStringNode(CharSequence charSequence, boolean escape){
return new StringNode(charSequence, escape);
}
public static ClassNode createClassNode(Class> clazz){
return new ClassNode(clazz);
}
public static NumberNode createNumberNode(CharSequence charSequence){
return new NumberNode(charSequence);
}
public static BooleanNode createBooleanNode(CharSequence charSequence){
return Boolean.parseBoolean(charSequence.toString()) ? TRUE : FALSE;
}
public static NullNode createNullNode(){
return NULL_NODE;
}
public static JsonNode createJsonNode(CharSequence json) {
return new JsonNode(json);
}
public static JsonNode createJsonNode(Object parsedJson) {
return new JsonNode(parsedJson);
}
public static PatternNode createPatternNode(CharSequence pattern) {
return new PatternNode(pattern);
}
public static PatternNode createPatternNode(Pattern pattern) {
return new PatternNode(pattern);
}
//workaround for issue: https://github.com/json-path/JsonPath/issues/613
public static OffsetDateTimeNode createOffsetDateTimeNode(CharSequence charSequence){
return new OffsetDateTimeNode(charSequence);
}
public static UndefinedNode createUndefinedNode() {
return UNDEFINED;
}
public static PathNode createPathNode(CharSequence path, boolean existsCheck, boolean shouldExists) {
return new PathNode(path, existsCheck, shouldExists);
}
public static ValueNode createPathNode(Path path) {
return new PathNode(path);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java
================================================
package com.jayway.jsonpath.internal.filter;
import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.regex.Pattern;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.path.PathCompiler;
import com.jayway.jsonpath.internal.path.PredicateContextImpl;
import com.jayway.jsonpath.spi.json.JsonProvider;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Moved these nodes out of the ValueNode abstract class.
* This is to avoid this possible issue:
*
* Classes that refer to their own subclasses in their static initializers or in static fields.
* Such references can cause JVM-level deadlocks in multithreaded environment, when
* one thread tries to load superclass and another thread tries to load subclass at the same time.
*/
public interface ValueNodes {
NullNode NULL_NODE = new NullNode();
BooleanNode TRUE = new BooleanNode("true");
BooleanNode FALSE = new BooleanNode("false");
UndefinedNode UNDEFINED = new UndefinedNode();
//----------------------------------------------------
//
// ValueNode Implementations
//
//----------------------------------------------------
class PatternNode extends ValueNode {
private final String pattern;
private final Pattern compiledPattern;
private final String flags;
PatternNode(CharSequence charSequence) {
String tmp = charSequence.toString();
int begin = tmp.indexOf('/');
int end = tmp.lastIndexOf('/');
this.pattern = tmp.substring(begin + 1, end);
int flagsIndex = end + 1;
this.flags = tmp.length() > flagsIndex ? tmp.substring(flagsIndex) : "";
this.compiledPattern = Pattern.compile(pattern, PatternFlag.parseFlags(flags.toCharArray()));
}
PatternNode(Pattern pattern) {
this.pattern = pattern.pattern();
this.compiledPattern = pattern;
this.flags = PatternFlag.parseFlags(pattern.flags());
}
Pattern getCompiledPattern() {
return compiledPattern;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Void.TYPE;
}
public boolean isPatternNode() {
return true;
}
public PatternNode asPatternNode() {
return this;
}
@Override
public String toString() {
if(!pattern.startsWith("/")){
return "/" + pattern + "/" + flags;
} else {
return pattern;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PatternNode)) return false;
PatternNode that = (PatternNode) o;
return !(compiledPattern != null ? !compiledPattern.equals(that.compiledPattern) : that.compiledPattern != null);
}
}
class JsonNode extends ValueNode {
private final Object json;
private final boolean parsed;
JsonNode(CharSequence charSequence) {
json = charSequence.toString();
parsed = false;
}
JsonNode(Object parsedJson) {
json = parsedJson;
parsed = true;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
if(isArray(ctx)) return List.class;
else if(isMap(ctx)) return Map.class;
else if(parse(ctx) instanceof Number) return Number.class;
else if(parse(ctx) instanceof String) return String.class;
else if(parse(ctx) instanceof Boolean) return Boolean.class;
else return Void.class;
}
public boolean isJsonNode() {
return true;
}
public JsonNode asJsonNode() {
return this;
}
public ValueNode asValueListNode(Predicate.PredicateContext ctx){
if(!isArray(ctx)){
return UNDEFINED;
} else {
return new ValueListNode(Collections.unmodifiableList((List) parse(ctx)));
}
}
public Object parse(Predicate.PredicateContext ctx){
try {
return parsed ? json : new JSONParser(JSONParser.MODE_PERMISSIVE).parse(json.toString());
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}
public boolean isParsed() {
return parsed;
}
public Object getJson() {
return json;
}
public boolean isArray(Predicate.PredicateContext ctx) {
return parse(ctx) instanceof List;
}
public boolean isMap(Predicate.PredicateContext ctx) {
return parse(ctx) instanceof Map;
}
public int length(Predicate.PredicateContext ctx) {
return isArray(ctx) ? ((List>) parse(ctx)).size() : -1;
}
public boolean isEmpty(Predicate.PredicateContext ctx) {
if (isArray(ctx) || isMap(ctx)) return ((Collection>) parse(ctx)).size() == 0;
else if((parse(ctx) instanceof String)) return ((String)parse(ctx)).length() == 0;
return true;
}
@Override
public String toString() {
return json.toString();
}
public boolean equals(JsonNode jsonNode, Predicate.PredicateContext ctx) {
if (this == jsonNode) return true;
return !(json != null ? !json.equals(jsonNode.parse(ctx)) : jsonNode.json != null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof JsonNode)) return false;
JsonNode jsonNode = (JsonNode) o;
return !(json != null ? !json.equals(jsonNode.json) : jsonNode.json != null);
}
}
class StringNode extends ValueNode {
private final String string;
private boolean useSingleQuote = true;
StringNode(CharSequence charSequence, boolean escape) {
if (escape && charSequence.length() > 1) {
char open = charSequence.charAt(0);
char close = charSequence.charAt(charSequence.length()-1);
if (open == '\'' && close == '\'') {
charSequence = charSequence.subSequence(1, charSequence.length()-1);
} else if (open == '"' && close == '"') {
charSequence = charSequence.subSequence(1, charSequence.length()-1);
useSingleQuote = false;
}
string = Utils.unescape(charSequence.toString());
} else {
string = charSequence.toString();
}
}
@Override
public NumberNode asNumberNode() {
BigDecimal number = null;
try {
number = new BigDecimal(string);
} catch (NumberFormatException nfe){
return NumberNode.NAN;
}
return new NumberNode(number);
}
public String getString() {
return string;
}
public int length(){
return getString().length();
}
public boolean isEmpty(){
return getString().isEmpty();
}
public boolean contains(String str) {
return getString().contains(str);
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return String.class;
}
public boolean isStringNode() {
return true;
}
public StringNode asStringNode() {
return this;
}
@Override
public String toString() {
String quote = useSingleQuote ? "'" : "\"";
return quote + Utils.escape(string, true) + quote;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof StringNode) && !(o instanceof NumberNode)) return false;
StringNode that = ((ValueNode) o).asStringNode();
return !(string != null ? !string.equals(that.getString()) : that.getString() != null);
}
}
class NumberNode extends ValueNode {
public static NumberNode NAN = new NumberNode((BigDecimal)null);
private final BigDecimal number;
NumberNode(BigDecimal number) {
this.number = number;
}
NumberNode(CharSequence num) {
number = new BigDecimal(num.toString());
}
@Override
public StringNode asStringNode() {
return new StringNode(number.toString(), false);
}
public BigDecimal getNumber() {
return number;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Number.class;
}
public boolean isNumberNode() {
return true;
}
public NumberNode asNumberNode() {
return this;
}
@Override
public String toString() {
return number.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NumberNode) && !(o instanceof StringNode)) return false;
NumberNode that = ((ValueNode)o).asNumberNode();
if(that == NumberNode.NAN){
return false;
} else {
return number.compareTo(that.number) == 0;
}
}
}
//workaround for issue: https://github.com/json-path/JsonPath/issues/613
class OffsetDateTimeNode extends ValueNode {
private final OffsetDateTime dateTime;
OffsetDateTimeNode(OffsetDateTime dateTime) {
this.dateTime = dateTime;
}
OffsetDateTimeNode(CharSequence date) {
dateTime = OffsetDateTime.parse(date);
}
@Override
public StringNode asStringNode() {
return new StringNode(dateTime.toString(), false);
}
public OffsetDateTime getDate() {
return dateTime;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return OffsetDateTimeNode.class;
}
public boolean isOffsetDateTimeNode() {
return true;
}
public OffsetDateTimeNode asOffsetDateTimeNode() {
return this;
}
@Override
public String toString() {
return dateTime.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof OffsetDateTimeNode) && !(o instanceof StringNode)) return false;
OffsetDateTimeNode that = ((ValueNode)o).asOffsetDateTimeNode();
return dateTime.compareTo(that.dateTime) == 0;
}
}
class BooleanNode extends ValueNode {
private final Boolean value;
private BooleanNode(CharSequence boolValue) {
value = Boolean.parseBoolean(boolValue.toString());
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Boolean.class;
}
public boolean isBooleanNode() {
return true;
}
public BooleanNode asBooleanNode() {
return this;
}
public boolean getBoolean() {
return value;
}
@Override
public String toString() {
return value.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BooleanNode)) return false;
BooleanNode that = (BooleanNode) o;
return !(value != null ? !value.equals(that.value) : that.value != null);
}
}
class ClassNode extends ValueNode {
private final Class clazz;
ClassNode(Class clazz) {
this.clazz = clazz;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Class.class;
}
public boolean isClassNode() {
return true;
}
public ClassNode asClassNode() {
return this;
}
public Class getClazz() {
return clazz;
}
@Override
public String toString() {
return clazz.getName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassNode)) return false;
ClassNode that = (ClassNode) o;
return !(clazz != null ? !clazz.equals(that.clazz) : that.clazz != null);
}
}
class NullNode extends ValueNode {
private NullNode() {}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Void.class;
}
@Override
public boolean isNullNode() {
return true;
}
@Override
public NullNode asNullNode() {
return this;
}
@Override
public String toString() {
return "null";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NullNode)) return false;
return true;
}
}
class UndefinedNode extends ValueNode {
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public UndefinedNode asUndefinedNode() {
return this;
}
public boolean isUndefinedNode() {
return true;
}
@Override
public boolean equals(Object o) {
return false;
}
}
class PredicateNode extends ValueNode {
private final Predicate predicate;
public PredicateNode(Predicate predicate) {
this.predicate = predicate;
}
public Predicate getPredicate() {
return predicate;
}
public PredicateNode asPredicateNode() {
return this;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public boolean isPredicateNode() {
return true;
}
@Override
public boolean equals(Object o) {
return false;
}
@Override
public String toString() {
return predicate.toString();
}
}
class ValueListNode extends ValueNode implements Iterable {
private List nodes = new ArrayList();
public ValueListNode(Collection> values) {
for (Object value : values) {
nodes.add(toValueNode(value));
}
}
public boolean contains(ValueNode node){
return nodes.contains(node);
}
public boolean subsetof(ValueListNode right) {
for (ValueNode leftNode : nodes) {
if (!right.nodes.contains(leftNode)) {
return false;
}
}
return true;
}
public List getNodes() {
return Collections.unmodifiableList(nodes);
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return List.class;
}
public boolean isValueListNode() {
return true;
}
public ValueListNode asValueListNode() {
return this;
}
@Override
public String toString() {
return "[" + Utils.join(",", nodes) + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ValueListNode)) return false;
ValueListNode that = (ValueListNode) o;
return nodes.equals(that.nodes);
}
@Override
public Iterator iterator() {
return nodes.iterator();
}
}
class PathNode extends ValueNode {
private static final Logger logger = LoggerFactory.getLogger(PathNode.class);
private final Path path;
private final boolean existsCheck;
private final boolean shouldExist;
PathNode(Path path) {
this(path, false, false);
}
PathNode(CharSequence charSequence, boolean existsCheck, boolean shouldExist) {
this(PathCompiler.compile(charSequence.toString()), existsCheck, shouldExist);
}
PathNode(Path path, boolean existsCheck, boolean shouldExist) {
this.path = path;
this.existsCheck = existsCheck;
this.shouldExist = shouldExist;
logger.trace("PathNode {} existsCheck: {}", path, existsCheck);
}
public Path getPath() {
return path;
}
public boolean isExistsCheck() {
return existsCheck;
}
public boolean shouldExists() {
return shouldExist;
}
@Override
public Class> type(Predicate.PredicateContext ctx) {
return Void.class;
}
public boolean isPathNode() {
return true;
}
public PathNode asPathNode() {
return this;
}
public PathNode asExistsCheck(boolean shouldExist) {
return new PathNode(path, true, shouldExist);
}
@Override
public String toString() {
return existsCheck && ! shouldExist ? Utils.concat("!" , path.toString()) : path.toString();
}
public ValueNode evaluate(Predicate.PredicateContext ctx) {
if (isExistsCheck()) {
try {
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build();
Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false);
return result == JsonProvider.UNDEFINED ? FALSE : TRUE;
} catch (PathNotFoundException e) {
return FALSE;
}
} else {
try {
Object res;
if (ctx instanceof PredicateContextImpl) {
//This will use cache for document ($) queries
PredicateContextImpl ctxi = (PredicateContextImpl) ctx;
res = ctxi.evaluate(path);
} else {
Object doc = path.isRootPath() ? ctx.root() : ctx.item();
res = path.evaluate(doc, ctx.root(), ctx.configuration()).getValue();
}
res = ctx.configuration().jsonProvider().unwrap(res);
if (res instanceof Number) return ValueNode.createNumberNode(res.toString());
else if (res instanceof String) return ValueNode.createStringNode(res.toString(), false);
else if (res instanceof Boolean) return ValueNode.createBooleanNode(res.toString());
else if (res instanceof OffsetDateTime) return ValueNode.createOffsetDateTimeNode(res.toString()); //workaround for issue: https://github.com/json-path/JsonPath/issues/613
else if (res == null) return NULL_NODE;
else if (ctx.configuration().jsonProvider().isArray(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, List.class, ctx.configuration()));
else if (ctx.configuration().jsonProvider().isMap(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, Map.class, ctx.configuration()));
else throw new JsonPathException("Could not convert " + res.getClass().toString()+":"+ res.toString() + " to a ValueNode");
} catch (PathNotFoundException e) {
return UNDEFINED;
}
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/ParamType.java
================================================
package com.jayway.jsonpath.internal.function;
/**
* Created by mgreenwood on 12/11/15.
*/
public enum ParamType {
JSON,
PATH
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/Parameter.java
================================================
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.function.latebinding.ILateBindingValue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Defines a parameter as passed to a function with late binding support for lazy evaluation.
*/
public class Parameter {
private ParamType type;
private Path path;
private ILateBindingValue lateBinding;
private Boolean evaluated = false;
private String json;
public Parameter() {}
public Parameter(String json) {
this.json = json;
this.type = ParamType.JSON;
}
public Parameter(Path path) {
this.path = path;
this.type = ParamType.PATH;
}
public Object getValue() {
return lateBinding.get();
}
public void setLateBinding(ILateBindingValue lateBinding) {
this.lateBinding = lateBinding;
}
public Path getPath() {
return path;
}
public void setEvaluated(Boolean evaluated) {
this.evaluated = evaluated;
}
public boolean hasEvaluated() {
return evaluated;
}
public ParamType getType() {
return type;
}
public void setType(ParamType type) {
this.type = type;
}
public void setPath(Path path) {
this.path = path;
}
public String getJson() {
return json;
}
public void setJson(String json) {
this.json = json;
}
public ILateBindingValue getILateBingValue(){
return lateBinding;
}
/**
* Translate the collection of parameters into a collection of values of type T.
*
* @param type
* The type to translate the collection into.
*
* @param ctx
* Context.
*
* @param parameters
* Collection of parameters.
*
* @param
* Type T returned as a List of T.
*
* @return
* List of T either empty or containing contents.
*/
public static List toList(final Class type, final EvaluationContext ctx, final List parameters) {
List values = new ArrayList();
if (null != parameters) {
for (Parameter param : parameters) {
consume(type, ctx, values, param.getValue());
}
}
return values;
}
/**
* Either consume the object as an array and add each element to the collection, or alternatively add each element
*
* @param expectedType
* the expected class type to consume, if null or not of this type the element is not added to the array.
*
* @param ctx
* the JSON context to determine if this is an array or value.
*
* @param collection
* The collection to append into.
*
* @param value
* The value to evaluate.
*/
public static void consume(Class expectedType, EvaluationContext ctx, Collection collection, Object value) {
if (ctx.configuration().jsonProvider().isArray(value)) {
for (Object o : ctx.configuration().jsonProvider().toIterable(value)) {
if (o != null && expectedType.isAssignableFrom(o.getClass())) {
collection.add(o);
} else if (o != null && expectedType == String.class) {
collection.add(o.toString());
}
}
} else {
if (value != null && expectedType.isAssignableFrom(value.getClass())) {
collection.add(value);
} else if (value != null && expectedType == String.class) {
collection.add(value.toString());
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/PassthruPathFunction.java
================================================
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import java.util.List;
/**
* Defines the default behavior which is to return the model that is provided as input as output
*
* Created by mattg on 6/26/15.
*/
public class PassthruPathFunction implements PathFunction {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
return model;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunction.java
================================================
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import java.util.List;
/**
* Defines the pattern by which a function can be executed over the result set in the particular path
* being grabbed. The Function's input is the content of the data from the json path selector and its output
* is defined via the functions behavior. Thus transformations in types can take place. Additionally, functions
* can accept multiple selectors in order to produce their output.
*
* Created by matt@mjgreenwood.net on 6/26/15.
*/
public interface PathFunction {
/**
* Invoke the function and output a JSON object (or scalar) value which will be the result of executing the path
*
* @param currentPath
* The current path location inclusive of the function name
* @param parent
* The path location above the current function
*
* @param model
* The JSON model as input to this particular function
*
* @param ctx
* Eval context, state bag used as the path is traversed, maintains the result of executing
*
* @param parameters
* @return result
*/
Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java
================================================
package com.jayway.jsonpath.internal.function;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.function.json.Append;
import com.jayway.jsonpath.internal.function.json.KeySetFunction;
import com.jayway.jsonpath.internal.function.numeric.Average;
import com.jayway.jsonpath.internal.function.numeric.Max;
import com.jayway.jsonpath.internal.function.numeric.Min;
import com.jayway.jsonpath.internal.function.numeric.StandardDeviation;
import com.jayway.jsonpath.internal.function.numeric.Sum;
import com.jayway.jsonpath.internal.function.sequence.First;
import com.jayway.jsonpath.internal.function.sequence.Index;
import com.jayway.jsonpath.internal.function.sequence.Last;
import com.jayway.jsonpath.internal.function.text.Concatenate;
import com.jayway.jsonpath.internal.function.text.Length;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Implements a factory that given a name of the function will return the Function implementation, or null
* if the value is not obtained.
*
* Leverages the function's name in order to determine which function to execute which is maintained internally
* here via a static map
*
*/
public class PathFunctionFactory {
public static final Map FUNCTIONS;
static {
// New functions should be added here and ensure the name is not overridden
Map map = new HashMap();
// Math Functions
map.put("avg", Average.class);
map.put("stddev", StandardDeviation.class);
map.put("sum", Sum.class);
map.put("min", Min.class);
map.put("max", Max.class);
// Text Functions
map.put("concat", Concatenate.class);
// JSON Entity Functions
map.put(Length.TOKEN_NAME, Length.class);
map.put("size", Length.class);
map.put("append", Append.class);
map.put("keys", KeySetFunction.class);
// Sequential Functions
map.put("first", First.class);
map.put("last", Last.class);
map.put("index", Index.class);
FUNCTIONS = Collections.unmodifiableMap(map);
}
/**
* Returns the function by name or throws InvalidPathException if function not found.
*
* @see #FUNCTIONS
* @see PathFunction
*
* @param name
* The name of the function
*
* @return
* The implementation of a function
*
* @throws InvalidPathException
*/
public static PathFunction newFunction(String name) throws InvalidPathException {
Class functionClazz = FUNCTIONS.get(name);
if(functionClazz == null){
throw new InvalidPathException("Function with name: " + name + " does not exist.");
} else {
try {
return (PathFunction)functionClazz.newInstance();
} catch (Exception e) {
throw new InvalidPathException("Function of name: " + name + " cannot be created", e);
}
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/json/Append.java
================================================
package com.jayway.jsonpath.internal.function.json;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.List;
/**
* Appends JSON structure to the current document so that you can utilize the JSON added thru another function call.
* If there are multiple parameters then this function call will add each element that is json to the structure
*
* Created by mgreenwood on 12/14/15.
*/
public class Append implements PathFunction {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
JsonProvider jsonProvider = ctx.configuration().jsonProvider();
if (parameters != null && parameters.size() > 0) {
for (Parameter param : parameters) {
if (jsonProvider.isArray(model)) {
int len = jsonProvider.length(model);
jsonProvider.setArrayIndex(model, len, param.getValue());
}
}
}
return model;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/json/KeySetFunction.java
================================================
package com.jayway.jsonpath.internal.function.json;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import java.util.List;
/**
* Author: Sergey Saiyan sergey.sova42@gmail.com
* Created at 21/02/2018.
*/
public class KeySetFunction implements PathFunction {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
if (ctx.configuration().jsonProvider().isMap(model)) {
return ctx.configuration().jsonProvider().getPropertyKeys(model);
}
return null;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/ILateBindingValue.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.function.latebinding;
/**
* Obtain the late binding value at runtime rather than storing the value in the cache thus trashing the cache
*
*/
public interface ILateBindingValue {
/**
* Obtain the value of the parameter at runtime using the parameter state and invocation of other late binding values
* rather than maintaining cached state which ends up in a global store and won't change as a result of external
* reference changes.
*
* @return
* The value of evaluating the context at runtime.
*/
Object get();
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/JsonLateBindingValue.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.function.latebinding;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.spi.json.JsonProvider;
/**
* Defines the JSON document Late binding approach to function arguments.
*
*/
public class JsonLateBindingValue implements ILateBindingValue {
private final JsonProvider jsonProvider;
private final Parameter jsonParameter;
public JsonLateBindingValue(JsonProvider jsonProvider, Parameter jsonParameter) {
this.jsonProvider = jsonProvider;
this.jsonParameter = jsonParameter;
}
/**
* Evaluate the JSON document at the point of need using the JSON parameter and associated document model which may
* itself originate from yet another function thus recursively invoking late binding methods.
*
* @return the late value
*/
@Override
public Object get() {
return jsonProvider.parse(jsonParameter.getJson());
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/latebinding/PathLateBindingValue.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.function.latebinding;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.internal.Path;
import java.util.Objects;
/**
* Defines the contract for late bindings, provides document state and enough context to perform the evaluation at a later
* date such that we can operate on a dynamically changing value.
*
* Acts like a lambda function with references, but since we're supporting JDK 6+, we're left doing this...
*
*/
public class PathLateBindingValue implements ILateBindingValue {
private final Path path;
private final String rootDocument;
private final Configuration configuration;
private final Object result;
public PathLateBindingValue(final Path path, final Object rootDocument, final Configuration configuration) {
this.path = path;
this.rootDocument = rootDocument.toString();
this.configuration = configuration;
this.result = path.evaluate(rootDocument, rootDocument, configuration).getValue();
}
/**
*
* @return the late value
*/
public Object get() {
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PathLateBindingValue that = (PathLateBindingValue) o;
return Objects.equals(path, that.path) &&
rootDocument.equals(that.rootDocument) &&
Objects.equals(configuration, that.configuration);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/AbstractAggregation.java
================================================
package com.jayway.jsonpath.internal.function.numeric;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import java.util.List;
/**
* Defines the pattern for processing numerical values via an abstract implementation that iterates over the collection
* of JSONArray entities and verifies that each is a numerical value and then passes that along the abstract methods
*
*
* Created by mattg on 6/26/15.
*/
public abstract class AbstractAggregation implements PathFunction {
/**
* Defines the next value in the array to the mathmatical function
*
* @param value
* The numerical value to process next
*/
protected abstract void next(Number value);
/**
* Obtains the value generated via the series of next value calls
*
* @return
* A numerical answer based on the input value provided
*/
protected abstract Number getValue();
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
int count = 0;
if(ctx.configuration().jsonProvider().isArray(model)){
Iterable> objects = ctx.configuration().jsonProvider().toIterable(model);
for (Object obj : objects) {
if (obj instanceof Number) {
Number value = (Number) obj;
count++;
next(value);
}
}
}
if (parameters != null) {
for (Number value : Parameter.toList(Number.class, ctx, parameters)) {
count++;
next(value);
}
}
if (count != 0) {
return getValue();
}
throw new JsonPathException("Aggregation function attempted to calculate value using empty array");
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Average.java
================================================
package com.jayway.jsonpath.internal.function.numeric;
/**
* Provides the average of a series of numbers in a JSONArray
*
* Created by mattg on 6/26/15.
*/
public class Average extends AbstractAggregation {
private Double summation = 0d;
private Double count = 0d;
@Override
protected void next(Number value) {
count++;
summation += value.doubleValue();
}
@Override
protected Number getValue() {
if (count != 0d) {
return summation / count;
}
return 0d;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Max.java
================================================
package com.jayway.jsonpath.internal.function.numeric;
/**
* Defines the summation of a series of JSONArray numerical values
*
* Created by mattg on 6/26/15.
*/
public class Max extends AbstractAggregation {
private Double max = Double.MIN_VALUE;
@Override
protected void next(Number value) {
if (max < value.doubleValue()) {
max = value.doubleValue();
}
}
@Override
protected Number getValue() {
return max;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Min.java
================================================
package com.jayway.jsonpath.internal.function.numeric;
/**
* Defines the summation of a series of JSONArray numerical values
*
* Created by mattg on 6/26/15.
*/
public class Min extends AbstractAggregation {
private Double min = Double.MAX_VALUE;
@Override
protected void next(Number value) {
if (min > value.doubleValue()) {
min = value.doubleValue();
}
}
@Override
protected Number getValue() {
return min;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/StandardDeviation.java
================================================
package com.jayway.jsonpath.internal.function.numeric;
/**
* Provides the standard deviation of a series of numbers
*
* Created by mattg on 6/27/15.
*/
public class StandardDeviation extends AbstractAggregation {
private Double sumSq = 0d;
private Double sum = 0d;
private Double count = 0d;
@Override
protected void next(Number value) {
sum += value.doubleValue();
sumSq += value.doubleValue() * value.doubleValue();
count++;
}
@Override
protected Number getValue() {
return Math.sqrt(sumSq/count - sum*sum/count/count);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/numeric/Sum.java
================================================
package com.jayway.jsonpath.internal.function.numeric;
/**
* Defines the summation of a series of JSONArray numerical values
*
* Created by mattg on 6/26/15.
*/
public class Sum extends AbstractAggregation {
private Double summation = 0d;
@Override
protected void next(Number value) {
summation += value.doubleValue();
}
@Override
protected Number getValue() {
return summation;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/AbstractSequenceAggregation.java
================================================
package com.jayway.jsonpath.internal.function.sequence;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import java.util.ArrayList;
import java.util.List;
/**
* Defines the pattern for taking item from collection of JSONArray by index
*
* Created by git9527 on 6/11/22.
*/
public abstract class AbstractSequenceAggregation implements PathFunction {
protected abstract int targetIndex(EvaluationContext ctx, List parameters);
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
if(ctx.configuration().jsonProvider().isArray(model)){
Iterable> objects = ctx.configuration().jsonProvider().toIterable(model);
List objectList = new ArrayList<>();
objects.forEach(objectList::add);
int targetIndex = this.targetIndex(ctx, parameters);
if (targetIndex >= 0) {
return objectList.get(targetIndex);
} else {
int realIndex = objectList.size() + targetIndex;
if (realIndex > 0) {
return objectList.get(realIndex);
} else {
throw new JsonPathException("Target index:" + targetIndex + " larger than object count:" + objectList.size());
}
}
}
throw new JsonPathException("Aggregation function attempted to calculate value using empty array");
}
protected int getIndexFromParameters(EvaluationContext ctx, List parameters) {
List numbers = Parameter.toList(Number.class, ctx, parameters);
return numbers.get(0).intValue();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/First.java
================================================
package com.jayway.jsonpath.internal.function.sequence;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.function.Parameter;
import java.util.List;
/**
* Take the first item from collection of JSONArray
*
* Created by git9527 on 6/11/22.
*/
public class First extends AbstractSequenceAggregation {
@Override
protected int targetIndex(EvaluationContext ctx, List parameters) {
return 0;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/Index.java
================================================
package com.jayway.jsonpath.internal.function.sequence;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.function.Parameter;
import java.util.List;
/**
* Take the index from first Parameter, then the item from collection of JSONArray by index
*
* Created by git9527 on 6/11/22.
*/
public class Index extends AbstractSequenceAggregation {
@Override
protected int targetIndex(EvaluationContext ctx, List parameters) {
return getIndexFromParameters(ctx, parameters);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/sequence/Last.java
================================================
package com.jayway.jsonpath.internal.function.sequence;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.function.Parameter;
import java.util.List;
/**
* Take the first item from collection of JSONArray
*
* Created by git9527 on 6/11/22.
*/
public class Last extends AbstractSequenceAggregation {
@Override
protected int targetIndex(EvaluationContext ctx, List parameters) {
return -1;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java
================================================
package com.jayway.jsonpath.internal.function.text;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import java.util.List;
/**
* String function concat - simple takes a list of arguments and/or an array and concatenates them together to form a
* single string
*
*/
public class Concatenate implements PathFunction {
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
StringBuilder result = new StringBuilder();
if(ctx.configuration().jsonProvider().isArray(model)){
Iterable> objects = ctx.configuration().jsonProvider().toIterable(model);
for (Object obj : objects) {
if (obj instanceof String) {
result.append(obj.toString());
}
}
}
if (parameters != null) {
for (String value : Parameter.toList(String.class, ctx, parameters)) {
result.append(value);
}
}
return result.toString();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Length.java
================================================
package com.jayway.jsonpath.internal.function.text;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.internal.path.CompiledPath;
import com.jayway.jsonpath.internal.path.PathToken;
import com.jayway.jsonpath.internal.path.RootPathToken;
import com.jayway.jsonpath.internal.path.WildcardPathToken;
import java.util.List;
/**
* Provides the length of a JSONArray Object
*
* Created by mattg on 6/26/15.
*/
public class Length implements PathFunction {
public static final String TOKEN_NAME = "length";
/**
* When we calculate the length of a path, what we're asking is given the node we land on how many children does it
* have. Thus when we wrote the original query what we really wanted was $..book.length() or $.length($..book.*)
*
* @param currentPath
* The current path location inclusive of the function name
* @param parent
* The path location above the current function
*
* @param model
* The JSON model as input to this particular function
*
* @param ctx
* Eval context, state bag used as the path is traversed, maintains the result of executing
*
* @param parameters
* @return
*/
@Override
public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) {
if (null != parameters && parameters.size() > 0) {
// Set the tail of the first parameter, when its not a function path parameter (which wouldn't make sense
// for length - to the wildcard such that we request all of its children so we can get back an array and
// take its length.
Parameter lengthOfParameter = parameters.get(0);
if (!lengthOfParameter.getPath().isFunctionPath()) {
Path path = lengthOfParameter.getPath();
if (path instanceof CompiledPath) {
RootPathToken root = ((CompiledPath) path).getRoot();
PathToken tail = root.getNext();
while (null != tail && null != tail.getNext()) {
tail = tail.getNext();
}
if (null != tail) {
tail.setNext(new WildcardPathToken());
}
}
}
Object innerModel = parameters.get(0).getPath().evaluate(model, model, ctx.configuration()).getValue();
if (ctx.configuration().jsonProvider().isArray(innerModel)) {
return ctx.configuration().jsonProvider().length(innerModel);
}
}
if (ctx.configuration().jsonProvider().isArray(model)) {
return ctx.configuration().jsonProvider().length(model);
} else if(ctx.configuration().jsonProvider().isMap(model)){
return ctx.configuration().jsonProvider().length(model);
}
return null;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexOperation.java
================================================
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.Utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import static java.lang.Character.isDigit;
public class ArrayIndexOperation {
private final static Pattern COMMA = Pattern.compile("\\s*,\\s*");
private final List indexes;
private ArrayIndexOperation(List indexes) {
this.indexes = Collections.unmodifiableList(indexes);
}
public List indexes() {
return indexes;
}
public boolean isSingleIndexOperation(){
return indexes.size() == 1;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(Utils.join(",", indexes));
sb.append("]");
return sb.toString();
}
public static ArrayIndexOperation parse(String operation) {
//check valid chars
for (int i = 0; i < operation.length(); i++) {
char c = operation.charAt(i);
if (!isDigit(c) && c != ',' && c != ' ' && c != '-') {
throw new InvalidPathException("Failed to parse ArrayIndexOperation: " + operation);
}
}
String[] tokens = COMMA.split(operation, -1);
List tempIndexes = new ArrayList(tokens.length);
for (String token : tokens) {
tempIndexes.add(parseInteger(token));
}
return new ArrayIndexOperation(tempIndexes);
}
private static Integer parseInteger(String token) {
try {
return Integer.parseInt(token);
} catch (Exception e){
throw new InvalidPathException("Failed to parse token in ArrayIndexOperation: " + token, e);
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.PathRef;
import static java.lang.String.format;
public class ArrayIndexToken extends ArrayPathToken {
private final ArrayIndexOperation arrayIndexOperation;
ArrayIndexToken(final ArrayIndexOperation arrayIndexOperation) {
this.arrayIndexOperation = arrayIndexOperation;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (!checkArrayModel(currentPath, model, ctx))
return;
if (arrayIndexOperation.isSingleIndexOperation()) {
handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx);
} else {
for (Integer index : arrayIndexOperation.indexes()) {
handleArrayIndex(index, currentPath, model, ctx);
}
}
}
@Override
public String getPathFragment() {
return arrayIndexOperation.toString();
}
@Override
public boolean isTokenDefinite() {
return arrayIndexOperation.isSingleIndexOperation();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import static java.lang.String.format;
public abstract class ArrayPathToken extends PathToken {
/**
* Check if model is non-null and array.
* @param currentPath
* @param model
* @param ctx
* @return false if current evaluation call must be skipped, true otherwise
* @throws PathNotFoundException if model is null and evaluation must be interrupted
* @throws InvalidPathException if model is not an array and evaluation must be interrupted
*/
protected boolean checkArrayModel(String currentPath, Object model, EvaluationContextImpl ctx) {
if (model == null){
if (!isUpstreamDefinite()
|| ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)) {
return false;
} else {
throw new PathNotFoundException("The path " + currentPath + " is null");
}
}
if (!ctx.jsonProvider().isArray(model)) {
if (!isUpstreamDefinite()
|| ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)) {
return false;
} else {
throw new PathNotFoundException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model));
}
}
return true;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java
================================================
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.InvalidPathException;
import static java.lang.Character.isDigit;
public class ArraySliceOperation {
public enum Operation {
SLICE_FROM,
SLICE_TO,
SLICE_BETWEEN
}
private final Integer from;
private final Integer to;
private final Operation operation;
private ArraySliceOperation(Integer from, Integer to, Operation operation) {
this.from = from;
this.to = to;
this.operation = operation;
}
public Integer from() {
return from;
}
public Integer to() {
return to;
}
public Operation operation() {
return operation;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(from == null ? "" : from.toString());
sb.append(":");
sb.append(to == null ? "" : to.toString());
sb.append("]");
return sb.toString();
}
public static ArraySliceOperation parse(String operation){
//check valid chars
for (int i = 0; i < operation.length(); i++) {
char c = operation.charAt(i);
if( !isDigit(c) && c != '-' && c != ':'){
throw new InvalidPathException("Failed to parse SliceOperation: " + operation);
}
}
String[] tokens = operation.split(":");
Integer tempFrom = tryRead(tokens, 0);
Integer tempTo = tryRead(tokens, 1);
Operation tempOperation;
if (tempFrom != null && tempTo == null) {
tempOperation = Operation.SLICE_FROM;
} else if (tempFrom != null) {
tempOperation = Operation.SLICE_BETWEEN;
} else if (tempTo != null) {
tempOperation = Operation.SLICE_TO;
} else {
throw new InvalidPathException("Failed to parse SliceOperation: " + operation);
}
return new ArraySliceOperation(tempFrom, tempTo, tempOperation);
}
private static Integer tryRead(String[] tokens, int idx){
if(tokens.length > idx){
if(tokens[idx].equals("")){
return null;
}
return Integer.parseInt(tokens[idx]);
} else {
return null;
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.PathRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ArraySliceToken extends ArrayPathToken {
private static final Logger logger = LoggerFactory.getLogger(ArraySliceToken.class);
private final ArraySliceOperation operation;
ArraySliceToken(final ArraySliceOperation operation) {
this.operation = operation;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (!checkArrayModel(currentPath, model, ctx))
return;
switch (operation.operation()) {
case SLICE_FROM:
sliceFrom(currentPath, parent, model, ctx);
break;
case SLICE_BETWEEN:
sliceBetween(currentPath, parent, model, ctx);
break;
case SLICE_TO:
sliceTo(currentPath, parent, model, ctx);
break;
}
}
private void sliceFrom(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
if (from < 0) {
//calculate slice start from array length
from = length + from;
}
from = Math.max(0, from);
logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString());
if (length == 0 || from >= length) {
return;
}
for (int i = from; i < length; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
private void sliceBetween(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
int to = operation.to();
to = Math.min(length, to);
if (from >= to || length == 0) {
return;
}
logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString());
for (int i = from; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
private void sliceTo(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
if (length == 0) {
return;
}
int to = operation.to();
if (to < 0) {
//calculate slice end from array length
to = length + to;
}
to = Math.min(length, to);
logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString());
for (int i = 0; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
@Override
public String getPathFragment() {
return operation.toString();
}
@Override
public boolean isTokenDefinite() {
return false;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.internal.EvaluationAbortException;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.ParamType;
import com.jayway.jsonpath.internal.function.Parameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
public class CompiledPath implements Path {
private static final Logger logger = LoggerFactory.getLogger(CompiledPath.class);
private final RootPathToken root;
private final boolean isRootPath;
public CompiledPath(RootPathToken root, boolean isRootPath) {
this.root = invertScannerFunctionRelationship(root);
this.isRootPath = isRootPath;
}
@Override
public boolean isRootPath() {
return isRootPath;
}
/**
* In the event the writer of the path referenced a function at the tail end of a scanner, augment the query such
* that the root node is the function and the parameter to the function is the scanner. This way we maintain
* relative sanity in the path expression, functions either evaluate scalar values or arrays, they're
* not re-entrant nor should they maintain state, they do however take parameters.
*
* @param path
* this is our old root path which will become a parameter (assuming there's a scanner terminated by a function
*
* @return
* A function with the scanner as input, or if this situation doesn't exist just the input path
*/
private RootPathToken invertScannerFunctionRelationship(final RootPathToken path) {
if (path.isFunctionPath() && path.next() instanceof ScanPathToken) {
PathToken token = path;
PathToken prior = null;
while (null != (token = token.next()) && !(token instanceof FunctionPathToken)) {
prior = token;
}
// Invert the relationship $..path.function() to $.function($..path)
if (token instanceof FunctionPathToken) {
prior.setNext(null);
path.setTail(prior);
// Now generate a new parameter from our path
Parameter parameter = new Parameter();
parameter.setPath(new CompiledPath(path, true));
parameter.setType(ParamType.PATH);
((FunctionPathToken)token).setParameters(Arrays.asList(parameter));
RootPathToken functionRoot = new RootPathToken('$');
functionRoot.setTail(token);
functionRoot.setNext(token);
// Define the function as the root
return functionRoot;
}
}
return path;
}
@Override
public EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration, boolean forUpdate) {
if (logger.isDebugEnabled()) {
logger.debug("Evaluating path: {}", toString());
}
EvaluationContextImpl ctx = new EvaluationContextImpl(this, rootDocument, configuration, forUpdate);
try {
PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP;
root.evaluate("", op, document, ctx);
} catch (EvaluationAbortException abort) {}
return ctx;
}
@Override
public EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration){
return evaluate(document, rootDocument, configuration, false);
}
@Override
public boolean isDefinite() {
return root.isPathDefinite();
}
@Override
public boolean isFunctionPath() {
return root.isFunctionPath();
}
@Override
public String toString() {
return root.toString();
}
public RootPathToken getRoot() {
return root;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluationContextImpl.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.EvaluationListener;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.EvaluationAbortException;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import static com.jayway.jsonpath.internal.Utils.notNull;
/**
*
*/
public class EvaluationContextImpl implements EvaluationContext {
private static final EvaluationAbortException ABORT_EVALUATION = new EvaluationAbortException();
private final Configuration configuration;
private final Object valueResult;
private final Object pathResult;
private final Path path;
private final Object rootDocument;
private final List updateOperations;
private final HashMap documentEvalCache = new HashMap();
private final boolean forUpdate;
private final boolean suppressExceptions;
private int resultIndex = 0;
public RootPathToken getRoot(){
return ((CompiledPath) path).getRoot();
}
public EvaluationContextImpl(Path path, Object rootDocument, Configuration configuration, boolean forUpdate) {
notNull(path, "path can not be null");
notNull(rootDocument, "root can not be null");
notNull(configuration, "configuration can not be null");
this.forUpdate = forUpdate;
this.path = path;
this.rootDocument = rootDocument;
this.configuration = configuration;
this.valueResult = configuration.jsonProvider().createArray();
this.pathResult = configuration.jsonProvider().createArray();
this.updateOperations = new ArrayList<>();
this.suppressExceptions = configuration.containsOption(Option.SUPPRESS_EXCEPTIONS);
}
public HashMap documentEvalCache() {
return documentEvalCache;
}
public boolean forUpdate(){
return forUpdate;
}
public void addResult(String path, PathRef operation, Object model) {
if(forUpdate) {
updateOperations.add(operation);
}
configuration.jsonProvider().setArrayIndex(valueResult, resultIndex, model);
configuration.jsonProvider().setArrayIndex(pathResult, resultIndex, path);
resultIndex++;
if(!configuration().getEvaluationListeners().isEmpty()){
int idx = resultIndex - 1;
for (EvaluationListener listener : configuration().getEvaluationListeners()) {
EvaluationListener.EvaluationContinuation continuation = listener.resultFound(new FoundResultImpl(idx, path, model));
if(EvaluationListener.EvaluationContinuation.ABORT == continuation){
throw ABORT_EVALUATION;
}
}
}
}
public JsonProvider jsonProvider() {
return configuration.jsonProvider();
}
public Set options() {
return configuration.getOptions();
}
@Override
public Configuration configuration() {
return configuration;
}
@Override
public Object rootDocument() {
return rootDocument;
}
public Collection updateOperations(){
Collections.sort(updateOperations);
return Collections.unmodifiableCollection(updateOperations);
}
@SuppressWarnings("unchecked")
@Override
public T getValue() {
return getValue(true);
}
@SuppressWarnings("unchecked")
@Override
public T getValue(boolean unwrap) {
if (path.isDefinite()) {
if(resultIndex == 0) {
if (suppressExceptions) {
return null;
}
throw new PathNotFoundException("No results for path: " + path.toString());
}
int len = jsonProvider().length(valueResult);
Object value = (len > 0) ? jsonProvider().getArrayIndex(valueResult, len-1) : null;
if (value != null && unwrap){
value = jsonProvider().unwrap(value);
}
return (T) value;
}
return (T)valueResult;
}
@SuppressWarnings("unchecked")
@Override
public T getPath() {
if(resultIndex == 0) {
if (suppressExceptions) {
return null;
}
throw new PathNotFoundException("No results for path: " + path.toString());
}
return (T)pathResult;
}
@Override
public List getPathList() {
List res = new ArrayList();
if(resultIndex > 0){
Iterable> objects = configuration.jsonProvider().toIterable(pathResult);
for (Object o : objects) {
res.add((String)o);
}
}
return res;
}
private static class FoundResultImpl implements EvaluationListener.FoundResult {
private final int index;
private final String path;
private final Object result;
private FoundResultImpl(int index, String path, Object result) {
this.index = index;
this.path = path;
this.result = result;
}
@Override
public int index() {
return index;
}
@Override
public String path() {
return path;
}
@Override
public Object result() {
return result;
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/FunctionPathToken.java
================================================
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.Parameter;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.internal.function.PathFunctionFactory;
import com.jayway.jsonpath.internal.function.latebinding.JsonLateBindingValue;
import com.jayway.jsonpath.internal.function.latebinding.PathLateBindingValue;
import java.util.List;
/**
* Token representing a Function call to one of the functions produced via the FunctionFactory
*
* @see PathFunctionFactory
*
* Created by mattg on 6/27/15.
*/
public class FunctionPathToken extends PathToken {
private final String functionName;
private final String pathFragment;
private List functionParams;
public FunctionPathToken(String pathFragment, List parameters) {
this.pathFragment = pathFragment + ((parameters != null && parameters.size() > 0) ? "(...)" : "()");
if(null != pathFragment){
functionName = pathFragment;
functionParams = parameters;
} else {
functionName = null;
functionParams = null;
}
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
PathFunction pathFunction = PathFunctionFactory.newFunction(functionName);
evaluateParameters(currentPath, parent, model, ctx);
Object result = pathFunction.invoke(currentPath, parent, model, ctx, functionParams);
ctx.addResult(currentPath + "." + functionName, parent, result);
cleanWildcardPathToken();
if (!isLeaf()) {
next().evaluate(currentPath, parent, result, ctx);
}
}
private void cleanWildcardPathToken() {
if (null != functionParams && functionParams.size() > 0) {
Path path = functionParams.get(0).getPath();
if (null != path && !path.isFunctionPath() && path instanceof CompiledPath) {
RootPathToken root = ((CompiledPath) path).getRoot();
PathToken tail = root.getNext();
while (null != tail && null != tail.getNext() ) {
if(tail.getNext() instanceof WildcardPathToken){
tail.setNext(tail.getNext().getNext());
break;
}
tail = tail.getNext();
}
}
}
}
private void evaluateParameters(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (null != functionParams) {
for (Parameter param : functionParams) {
switch (param.getType()) {
case PATH:
PathLateBindingValue pathLateBindingValue = new PathLateBindingValue(param.getPath(), ctx.rootDocument(), ctx.configuration());
if (!param.hasEvaluated()||!pathLateBindingValue.equals(param.getILateBingValue())) {
param.setLateBinding(pathLateBindingValue);
param.setEvaluated(true);
}
break;
case JSON:
if (!param.hasEvaluated()) {
param.setLateBinding(new JsonLateBindingValue(ctx.configuration().jsonProvider(), param));
param.setEvaluated(true);
}
break;
}
}
}
}
/**
* Return the actual value by indicating true. If this return was false then we'd return the value in an array which
* isn't what is desired - true indicates the raw value is returned.
*
* @return true if token is definite
*/
@Override
public boolean isTokenDefinite() {
return true;
}
@Override
public String getPathFragment() {
return "." + pathFragment;
}
public void setParameters(List parameters) {
this.functionParams = parameters;
}
public List getParameters() {
return this.functionParams;
}
public String getFunctionName() {
return this.functionName;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java
================================================
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.CharacterIndex;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.filter.FilterCompiler;
import com.jayway.jsonpath.internal.function.ParamType;
import com.jayway.jsonpath.internal.function.Parameter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import static java.lang.Character.isDigit;
import static java.util.Arrays.asList;
public class PathCompiler {
private static final char DOC_CONTEXT = '$';
private static final char EVAL_CONTEXT = '@';
private static final char OPEN_SQUARE_BRACKET = '[';
private static final char CLOSE_SQUARE_BRACKET = ']';
private static final char OPEN_PARENTHESIS = '(';
private static final char CLOSE_PARENTHESIS = ')';
private static final char OPEN_BRACE = '{';
private static final char CLOSE_BRACE = '}';
private static final char WILDCARD = '*';
private static final char PERIOD = '.';
private static final char SPACE = ' ';
private static final char TAB = '\t';
private static final char CR = '\r';
private static final char LF = '\n';
private static final char BEGIN_FILTER = '?';
private static final char COMMA = ',';
private static final char SPLIT = ':';
private static final char MINUS = '-';
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private final LinkedList filterStack;
private final CharacterIndex path;
private PathCompiler(String path, LinkedList filterStack){
this(new CharacterIndex(path), filterStack);
}
private PathCompiler(CharacterIndex path, LinkedList filterStack){
this.filterStack = filterStack;
this.path = path;
}
private Path compile() {
RootPathToken root = readContextToken();
return new CompiledPath(root, root.getPathFragment().equals("$"));
}
public static Path compile(String path, final Predicate... filters) {
try {
CharacterIndex ci = new CharacterIndex(path);
ci.trim();
if(!( ci.charAt(0) == DOC_CONTEXT) && !( ci.charAt(0) == EVAL_CONTEXT)){
ci = new CharacterIndex("$." + path);
ci.trim();
}
if(ci.lastCharIs('.')){
fail("Path must not end with a '.' or '..'");
}
LinkedList filterStack = new LinkedList(asList(filters));
return new PathCompiler(ci, filterStack).compile();
} catch (Exception e) {
InvalidPathException ipe;
if (e instanceof InvalidPathException) {
ipe = (InvalidPathException) e;
} else {
ipe = new InvalidPathException(e);
}
throw ipe;
}
}
private void readWhitespace() {
while (path.inBounds()) {
char c = path.currentChar();
if (!isWhitespace(c)) {
break;
}
path.incrementPosition(1);
}
}
private Boolean isPathContext(char c) {
return (c == DOC_CONTEXT || c == EVAL_CONTEXT);
}
//[$ | @]
private RootPathToken readContextToken() {
readWhitespace();
if (!isPathContext(path.currentChar())) {
throw new InvalidPathException("Path must start with '$' or '@'");
}
RootPathToken pathToken = PathTokenFactory.createRootPathToken(path.currentChar());
if (path.currentIsTail()) {
return pathToken;
}
path.incrementPosition(1);
if(path.currentChar() != PERIOD && path.currentChar() != OPEN_SQUARE_BRACKET){
fail("Illegal character at position " + path.position() + " expected '.' or '['");
}
PathTokenAppender appender = pathToken.getPathTokenAppender();
readNextToken(appender);
return pathToken;
}
//
//
//
private boolean readNextToken(PathTokenAppender appender) {
char c = path.currentChar();
switch (c) {
case OPEN_SQUARE_BRACKET:
if (!readBracketPropertyToken(appender) && !readArrayToken(appender) && !readWildCardToken(appender)
&& !readFilterToken(appender) && !readPlaceholderToken(appender)) {
fail("Could not parse token starting at position " + path.position() + ". Expected ?, ', 0-9, * ");
}
return true;
case PERIOD:
if (!readDotToken(appender)) {
fail("Could not parse token starting at position " + path.position());
}
return true;
case WILDCARD:
if (!readWildCardToken(appender)) {
fail("Could not parse token starting at position " + path.position());
}
return true;
default:
if (!readPropertyOrFunctionToken(appender)) {
fail("Could not parse token starting at position " + path.position());
}
return true;
}
}
//
// . and ..
//
private boolean readDotToken(PathTokenAppender appender) {
if (path.currentCharIs(PERIOD) && path.nextCharIs(PERIOD)) {
appender.appendPathToken(PathTokenFactory.crateScanToken());
path.incrementPosition(2);
} else if (!path.hasMoreCharacters()) {
throw new InvalidPathException("Path must not end with a '.");
} else {
path.incrementPosition(1);
}
if(path.currentCharIs(PERIOD)){
throw new InvalidPathException("Character '.' on position " + path.position() + " is not valid.");
}
return readNextToken(appender);
}
//
// fooBar or fooBar()
//
private boolean readPropertyOrFunctionToken(PathTokenAppender appender) {
if (path.currentCharIs(OPEN_SQUARE_BRACKET) || path.currentCharIs(WILDCARD) || path.currentCharIs(PERIOD) || path.currentCharIs(SPACE)) {
return false;
}
int startPosition = path.position();
int readPosition = startPosition;
int endPosition = 0;
boolean isFunction = false;
while (path.inBounds(readPosition)) {
char c = path.charAt(readPosition);
if (c == SPACE) {
throw new InvalidPathException("Use bracket notion ['my prop'] if your property contains blank characters. position: " + path.position());
}
else if (c == PERIOD || c == OPEN_SQUARE_BRACKET) {
endPosition = readPosition;
break;
}
else if (c == OPEN_PARENTHESIS) {
isFunction = true;
endPosition = readPosition;
break;
}
readPosition++;
}
if (endPosition == 0) {
endPosition = path.length();
}
List functionParameters = null;
if (isFunction) {
int parenthesis_count = 1;
for(int i = readPosition + 1; i < path.length(); i++){
if (path.charAt(i) == CLOSE_PARENTHESIS)
parenthesis_count--;
else if (path.charAt(i) == OPEN_PARENTHESIS)
parenthesis_count++;
if (parenthesis_count == 0)
break;
}
if (parenthesis_count != 0){
String functionName = path.subSequence(startPosition, endPosition).toString();
throw new InvalidPathException("Arguments to function: '" + functionName + "' are not closed properly.");
}
if (path.inBounds(readPosition+1)) {
// read the next token to determine if we have a simple no-args function call
char c = path.charAt(readPosition + 1);
if (c != CLOSE_PARENTHESIS) {
path.setPosition(endPosition+1);
// parse the arguments of the function - arguments that are inner queries or JSON document(s)
String functionName = path.subSequence(startPosition, endPosition).toString();
functionParameters = parseFunctionParameters(functionName);
} else {
path.setPosition(readPosition + 1);
}
}
else {
path.setPosition(readPosition);
}
}
else {
path.setPosition(endPosition);
}
String property = path.subSequence(startPosition, endPosition).toString();
if(isFunction){
appender.appendPathToken(PathTokenFactory.createFunctionPathToken(property, functionParameters));
} else {
appender.appendPathToken(PathTokenFactory.createSinglePropertyPathToken(property, SINGLE_QUOTE));
}
return path.currentIsTail() || readNextToken(appender);
}
/**
* Parse the parameters of a function call, either the caller has supplied JSON data, or the caller has supplied
* another path expression which must be evaluated and in turn invoked against the root document. In this tokenizer
* we're only concerned with parsing the path thus the output of this function is a list of parameters with the Path
* set if the parameter is an expression. If the parameter is a JSON document then the value of the cachedValue is
* set on the object.
*
* Sequence for parsing out the parameters:
*
* This code has its own tokenizer - it does some rudimentary level of lexing in that it can distinguish between JSON block parameters
* and sub-JSON blocks - it effectively regex's out the parameters into string blocks that can then be passed along to the appropriate parser.
* Since sub-jsonpath expressions can themselves contain other function calls this routine needs to be sensitive to token counting to
* determine the boundaries. Since the Path parser isn't aware of JSON processing this uber routine is needed.
*
* Parameters are separated by COMMAs ','
*
*
* doc = {"numbers": [1,2,3,4,5,6,7,8,9,10]}
*
* $.sum({10}, $.numbers.avg())
*
*
* The above is a valid function call, we're first summing 10 + avg of 1...10 (5.5) so the total should be 15.5
*
* @return
* An ordered list of parameters that are to processed via the function. Typically functions either process
* an array of values and/or can consume parameters in addition to the values provided from the consumption of
* an array.
*/
private List parseFunctionParameters(String funcName) {
ParamType type = null;
// Parenthesis starts at 1 since we're marking the start of a function call, the close paren will denote the
// last parameter boundary
int groupParen = 1, groupBracket = 0, groupBrace = 0, groupQuote = 0;
boolean endOfStream = false;
char priorChar = 0;
List parameters = new ArrayList();
StringBuilder parameter = new StringBuilder();
while (path.inBounds() && !endOfStream) {
char c = path.currentChar();
path.incrementPosition(1);
// we're at the start of the stream, and don't know what type of parameter we have
if (type == null) {
if (isWhitespace(c)) {
continue;
}
if (c == OPEN_BRACE || isDigit(c) || DOUBLE_QUOTE == c || MINUS == c) {
type = ParamType.JSON;
}
else if (isPathContext(c)) {
type = ParamType.PATH; // read until we reach a terminating comma and we've reset grouping to zero
}
}
switch (c) {
case DOUBLE_QUOTE:
if (priorChar != '\\' && groupQuote > 0) {
groupQuote--;
}
else {
groupQuote++;
}
break;
case OPEN_PARENTHESIS:
groupParen++;
break;
case OPEN_BRACE:
groupBrace++;
break;
case OPEN_SQUARE_BRACKET:
groupBracket++;
break;
case CLOSE_BRACE:
if (0 == groupBrace) {
throw new InvalidPathException("Unexpected close brace '}' at character position: " + path.position());
}
groupBrace--;
break;
case CLOSE_SQUARE_BRACKET:
if (0 == groupBracket) {
throw new InvalidPathException("Unexpected close bracket ']' at character position: " + path.position());
}
groupBracket--;
break;
// In either the close paren case where we have zero paren groups left, capture the parameter, or where
// we've encountered a COMMA do the same
case CLOSE_PARENTHESIS:
groupParen--;
//CS304 Issue link: https://github.com/json-path/JsonPath/issues/620
if (0 > groupParen || priorChar == '(') {
parameter.append(c);
}
case COMMA:
// In this state we've reach the end of a function parameter and we can pass along the parameter string
// to the parser
if ((0 == groupQuote && 0 == groupBrace && 0 == groupBracket
&& ((0 == groupParen && CLOSE_PARENTHESIS == c) || 1 == groupParen))) {
endOfStream = (0 == groupParen);
if (null != type) {
Parameter param = null;
switch (type) {
case JSON:
// parse the json and set the value
param = new Parameter(parameter.toString());
break;
case PATH:
LinkedList predicates = new LinkedList<>();
PathCompiler compiler = new PathCompiler(parameter.toString(), predicates);
param = new Parameter(compiler.compile());
break;
}
if (null != param) {
parameters.add(param);
}
parameter.delete(0, parameter.length());
type = null;
}
}
break;
}
if (type != null && !(c == COMMA && 0 == groupBrace && 0 == groupBracket && 1 == groupParen)) {
parameter.append(c);
}
priorChar = c;
}
if (0 != groupBrace || 0 != groupParen || 0 != groupBracket) {
throw new InvalidPathException("Arguments to function: '" + funcName + "' are not closed properly.");
}
return parameters;
}
private boolean isWhitespace(char c) {
return (c == SPACE || c == TAB || c == LF || c == CR);
}
//
// [?], [?,?, ..]
//
private boolean readPlaceholderToken(PathTokenAppender appender) {
if (!path.currentCharIs(OPEN_SQUARE_BRACKET)) {
return false;
}
int questionmarkIndex = path.indexOfNextSignificantChar(BEGIN_FILTER);
if (questionmarkIndex == -1) {
return false;
}
char nextSignificantChar = path.nextSignificantChar(questionmarkIndex);
if (nextSignificantChar != CLOSE_SQUARE_BRACKET && nextSignificantChar != COMMA) {
return false;
}
int expressionBeginIndex = path.position() + 1;
int expressionEndIndex = path.nextIndexOf(expressionBeginIndex, CLOSE_SQUARE_BRACKET);
if (expressionEndIndex == -1) {
return false;
}
String expression = path.subSequence(expressionBeginIndex, expressionEndIndex).toString();
String[] tokens = expression.split(",");
if (filterStack.size() < tokens.length) {
throw new InvalidPathException("Not enough predicates supplied for filter [" + expression + "] at position " + path.position());
}
Collection predicates = new ArrayList();
for (String token : tokens) {
token = token != null ? token.trim() : null;
if (!"?".equals(token == null ? "" : token)) {
throw new InvalidPathException("Expected '?' but found " + token);
}
predicates.add(filterStack.pop());
}
appender.appendPathToken(PathTokenFactory.createPredicatePathToken(predicates));
path.setPosition(expressionEndIndex + 1);
return path.currentIsTail() || readNextToken(appender);
}
//
// [?(...)]
//
private boolean readFilterToken(PathTokenAppender appender) {
if (!path.currentCharIs(OPEN_SQUARE_BRACKET) && !path.nextSignificantCharIs(BEGIN_FILTER)) {
return false;
}
int openStatementBracketIndex = path.position();
int questionMarkIndex = path.indexOfNextSignificantChar(BEGIN_FILTER);
if (questionMarkIndex == -1) {
return false;
}
int openBracketIndex = path.indexOfNextSignificantChar(questionMarkIndex, OPEN_PARENTHESIS);
if (openBracketIndex == -1) {
return false;
}
int closeBracketIndex = path.indexOfClosingBracket(openBracketIndex, true, true);
if (closeBracketIndex == -1) {
return false;
}
if (!path.nextSignificantCharIs(closeBracketIndex, CLOSE_SQUARE_BRACKET)) {
return false;
}
int closeStatementBracketIndex = path.indexOfNextSignificantChar(closeBracketIndex, CLOSE_SQUARE_BRACKET);
String criteria = path.subSequence(openStatementBracketIndex, closeStatementBracketIndex + 1).toString();
Predicate predicate = FilterCompiler.compile(criteria);
appender.appendPathToken(PathTokenFactory.createPredicatePathToken(predicate));
path.setPosition(closeStatementBracketIndex + 1);
return path.currentIsTail() || readNextToken(appender);
}
//
// [*]
// *
//
private boolean readWildCardToken(PathTokenAppender appender) {
boolean inBracket = path.currentCharIs(OPEN_SQUARE_BRACKET);
if (inBracket && !path.nextSignificantCharIs(WILDCARD)) {
return false;
}
if (!path.currentCharIs(WILDCARD) && path.isOutOfBounds(path.position() + 1)) {
return false;
}
if (inBracket) {
int wildCardIndex = path.indexOfNextSignificantChar(WILDCARD);
if (!path.nextSignificantCharIs(wildCardIndex, CLOSE_SQUARE_BRACKET)) {
int offset = wildCardIndex + 1;
throw new InvalidPathException("Expected wildcard token to end with ']' on position " + offset);
}
int bracketCloseIndex = path.indexOfNextSignificantChar(wildCardIndex, CLOSE_SQUARE_BRACKET);
path.setPosition(bracketCloseIndex + 1);
} else {
path.incrementPosition(1);
}
appender.appendPathToken(PathTokenFactory.createWildCardPathToken());
return path.currentIsTail() || readNextToken(appender);
}
//
// [1], [1,2, n], [1:], [1:2], [:2]
//
private boolean readArrayToken(PathTokenAppender appender) {
if (!path.currentCharIs(OPEN_SQUARE_BRACKET)) {
return false;
}
char nextSignificantChar = path.nextSignificantChar();
if (!isDigit(nextSignificantChar) && nextSignificantChar != MINUS && nextSignificantChar != SPLIT) {
return false;
}
int expressionBeginIndex = path.position() + 1;
int expressionEndIndex = path.nextIndexOf(expressionBeginIndex, CLOSE_SQUARE_BRACKET);
if (expressionEndIndex == -1) {
return false;
}
String expression = path.subSequence(expressionBeginIndex, expressionEndIndex).toString().trim();
if ("*".equals(expression)) {
return false;
}
//check valid chars
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (!isDigit(c) && c != COMMA && c != MINUS && c != SPLIT && c != SPACE) {
return false;
}
}
boolean isSliceOperation = expression.contains(":");
if (isSliceOperation) {
ArraySliceOperation arraySliceOperation = ArraySliceOperation.parse(expression);
appender.appendPathToken(PathTokenFactory.createSliceArrayPathToken(arraySliceOperation));
} else {
ArrayIndexOperation arrayIndexOperation = ArrayIndexOperation.parse(expression);
appender.appendPathToken(PathTokenFactory.createIndexArrayPathToken(arrayIndexOperation));
}
path.setPosition(expressionEndIndex + 1);
return path.currentIsTail() || readNextToken(appender);
}
//
// ['foo']
//
private boolean readBracketPropertyToken(PathTokenAppender appender) {
if (!path.currentCharIs(OPEN_SQUARE_BRACKET)) {
return false;
}
char potentialStringDelimiter = path.nextSignificantChar();
if (potentialStringDelimiter != SINGLE_QUOTE && potentialStringDelimiter != DOUBLE_QUOTE) {
return false;
}
List properties = new ArrayList();
int startPosition = path.position() + 1;
int readPosition = startPosition;
int endPosition = 0;
boolean inProperty = false;
boolean inEscape = false;
boolean lastSignificantWasComma = false;
while (path.inBounds(readPosition)) {
char c = path.charAt(readPosition);
if(inEscape){
inEscape = false;
} else if('\\' == c){
inEscape = true;
} else if (c == CLOSE_SQUARE_BRACKET && !inProperty) {
if (lastSignificantWasComma){
fail("Found empty property at index "+readPosition);
}
break;
} else if (c == potentialStringDelimiter) {
if (inProperty) {
char nextSignificantChar = path.nextSignificantChar(readPosition);
if (nextSignificantChar != CLOSE_SQUARE_BRACKET && nextSignificantChar != COMMA) {
fail("Property must be separated by comma or Property must be terminated close square bracket at index "+readPosition);
}
endPosition = readPosition;
String prop = path.subSequence(startPosition, endPosition).toString();
properties.add(Utils.unescape(prop));
inProperty = false;
} else {
startPosition = readPosition + 1;
inProperty = true;
lastSignificantWasComma = false;
}
} else if (c == COMMA && !inProperty) {
if (lastSignificantWasComma){
fail("Found empty property at index "+readPosition);
}
lastSignificantWasComma = true;
}
readPosition++;
}
if (inProperty){
fail("Property has not been closed - missing closing " + potentialStringDelimiter);
}
int endBracketIndex = path.indexOfNextSignificantChar(endPosition, CLOSE_SQUARE_BRACKET);
if(endBracketIndex == -1) {
fail("Property has not been closed - missing closing ]");
}
endBracketIndex++;
path.setPosition(endBracketIndex);
appender.appendPathToken(PathTokenFactory.createPropertyPathToken(properties, potentialStringDelimiter));
return path.currentIsTail() || readNextToken(appender);
}
public static boolean fail(String message) {
throw new InvalidPathException(message);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.List;
public abstract class PathToken {
private PathToken prev;
private PathToken next;
private Boolean definite = null;
private Boolean upstreamDefinite = null;
private int upstreamArrayIndex = -1;
public void setUpstreamArrayIndex(int idx){
upstreamArrayIndex = idx;
}
PathToken appendTailToken(PathToken next) {
this.next = next;
this.next.prev = this;
return next;
}
void handleObjectProperty(String currentPath, Object model, EvaluationContextImpl ctx, List properties) {
if(properties.size() == 1) {
String property = properties.get(0);
String evalPath = Utils.concat(currentPath, "['", property, "']");
Object propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
// Conditions below heavily depend on current token type (and its logic) and are not "universal",
// so this code is quite dangerous (I'd rather rewrite it & move to PropertyPathToken and implemented
// WildcardPathToken as a dynamic multi prop case of PropertyPathToken).
// Better safe than sorry.
assert this instanceof PropertyPathToken : "only PropertyPathToken is supported";
if(isLeaf()) {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
} else {
if(ctx.options().contains(Option.SUPPRESS_EXCEPTIONS) ||
!ctx.options().contains(Option.REQUIRE_PROPERTIES)){
return;
} else {
throw new PathNotFoundException("No results for path: " + evalPath);
}
}
} else {
if (! (isUpstreamDefinite() && isTokenDefinite()) &&
!ctx.options().contains(Option.REQUIRE_PROPERTIES) ||
ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)){
// If there is some indefiniteness in the path and properties are not required - we'll ignore
// absent property. And also in case of exception suppression - so that other path evaluation
// branches could be examined.
return;
} else {
throw new PathNotFoundException("Missing property in path " + evalPath);
}
}
}
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP;
if (isLeaf()) {
String idx = "[" + String.valueOf(upstreamArrayIndex) + "]";
if(idx.equals("[-1]") || ctx.getRoot().getTail().prev().getPathFragment().equals(idx)){
ctx.addResult(evalPath, pathRef, propertyVal);
}
}
else {
next().evaluate(evalPath, pathRef, propertyVal, ctx);
}
} else {
String evalPath = currentPath + "[" + Utils.join(", ", "'", properties) + "]";
assert isLeaf() : "non-leaf multi props handled elsewhere";
Object merged = ctx.jsonProvider().createMap();
for (String property : properties) {
Object propertyVal;
if(hasProperty(property, model, ctx)) {
propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) {
propertyVal = null;
} else {
continue;
}
}
} else {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
} else if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
throw new PathNotFoundException("Missing property in path " + evalPath);
} else {
continue;
}
}
ctx.jsonProvider().setProperty(merged, property, propertyVal);
}
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, properties) : PathRef.NO_OP;
ctx.addResult(evalPath, pathRef, merged);
}
}
private static boolean hasProperty(String property, Object model, EvaluationContextImpl ctx) {
return ctx.jsonProvider().getPropertyKeys(model).contains(property);
}
private static Object readObjectProperty(String property, Object model, EvaluationContextImpl ctx) {
return ctx.jsonProvider().getMapValue(model, property);
}
protected void handleArrayIndex(int index, String currentPath, Object model, EvaluationContextImpl ctx) {
String evalPath = Utils.concat(currentPath, "[", String.valueOf(index), "]");
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, index) : PathRef.NO_OP;
int effectiveIndex = index < 0 ? ctx.jsonProvider().length(model) + index : index;
try {
Object evalHit = ctx.jsonProvider().getArrayIndex(model, effectiveIndex);
if (isLeaf()) {
ctx.addResult(evalPath, pathRef, evalHit);
} else {
next().evaluate(evalPath, pathRef, evalHit, ctx);
}
} catch (IndexOutOfBoundsException e) {
}
}
PathToken prev(){
return prev;
}
PathToken next() {
if (isLeaf()) {
throw new IllegalStateException("Current path token is a leaf");
}
return next;
}
boolean isLeaf() {
return next == null;
}
boolean isRoot() {
return prev == null;
}
boolean isUpstreamDefinite() {
if (upstreamDefinite == null) {
upstreamDefinite = isRoot() || prev.isTokenDefinite() && prev.isUpstreamDefinite();
}
return upstreamDefinite;
}
public int getTokenCount() {
int cnt = 1;
PathToken token = this;
while (!token.isLeaf()){
token = token.next();
cnt++;
}
return cnt;
}
public boolean isPathDefinite() {
if(definite != null){
return definite.booleanValue();
}
boolean isDefinite = isTokenDefinite();
if (isDefinite && !isLeaf()) {
isDefinite = next.isPathDefinite();
}
definite = isDefinite;
return isDefinite;
}
@Override
public String toString() {
if (isLeaf()) {
return getPathFragment();
} else {
return getPathFragment() + next().toString();
}
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
public void invoke(PathFunction pathFunction, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
ctx.addResult(currentPath, parent, pathFunction.invoke(currentPath, parent, model, ctx, null));
}
public abstract void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx);
public abstract boolean isTokenDefinite();
protected abstract String getPathFragment();
public void setNext(final PathToken next) {
this.next = next;
}
public PathToken getNext() {
return this.next;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenAppender.java
================================================
package com.jayway.jsonpath.internal.path;
public interface PathTokenAppender {
PathTokenAppender appendPathToken(PathToken next);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java
================================================
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.function.Parameter;
import java.util.Collection;
import java.util.List;
import static java.util.Collections.singletonList;
public class PathTokenFactory {
public static RootPathToken createRootPathToken(char token) {
return new RootPathToken(token);
}
public static PathToken createSinglePropertyPathToken(String property, char stringDelimiter) {
return new PropertyPathToken(singletonList(property), stringDelimiter);
}
public static PathToken createPropertyPathToken(List properties, char stringDelimiter) {
return new PropertyPathToken(properties, stringDelimiter);
}
public static PathToken createSliceArrayPathToken(final ArraySliceOperation arraySliceOperation) {
return new ArraySliceToken(arraySliceOperation);
}
public static PathToken createIndexArrayPathToken(final ArrayIndexOperation arrayIndexOperation) {
return new ArrayIndexToken(arrayIndexOperation);
}
public static PathToken createWildCardPathToken() {
return new WildcardPathToken();
}
public static PathToken crateScanToken() {
return new ScanPathToken();
}
public static PathToken createPredicatePathToken(Collection predicates) {
return new PredicatePathToken(predicates);
}
public static PathToken createPredicatePathToken(Predicate predicate) {
return new PredicatePathToken(predicate);
}
public static PathToken createFunctionPathToken(String function, List parameters) {
return new FunctionPathToken(function, parameters);
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.spi.mapper.MappingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class PredicateContextImpl implements Predicate.PredicateContext {
private static final Logger logger = LoggerFactory.getLogger(PredicateContextImpl.class);
private final Object contextDocument;
private final Object rootDocument;
private final Configuration configuration;
private final HashMap documentPathCache;
public PredicateContextImpl(Object contextDocument, Object rootDocument, Configuration configuration, HashMap documentPathCache) {
this.contextDocument = contextDocument;
this.rootDocument = rootDocument;
this.configuration = configuration;
this.documentPathCache = documentPathCache;
}
public Object evaluate(Path path){
Object result;
if(path.isRootPath()){
if(documentPathCache.containsKey(path)){
logger.debug("Using cached result for root path: " + path.toString());
result = documentPathCache.get(path);
} else {
result = path.evaluate(rootDocument, rootDocument, configuration).getValue();
documentPathCache.put(path, result);
}
} else {
result = path.evaluate(contextDocument, rootDocument, configuration).getValue();
}
return result;
}
public HashMap documentPathCache() {
return documentPathCache;
}
@Override
public Object item() {
return contextDocument;
}
@Override
public T item(Class clazz) throws MappingException {
return configuration().mappingProvider().map(contextDocument, clazz, configuration);
}
@Override
public Object root() {
return rootDocument;
}
@Override
public Configuration configuration() {
return configuration;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.PathRef;
import java.util.Collection;
import java.util.Collections;
import static java.lang.String.format;
import static java.util.Arrays.asList;
/**
*
*/
public class PredicatePathToken extends PathToken {
private final Collection predicates;
PredicatePathToken(Predicate filter) {
this.predicates = Collections.singletonList(filter);
}
PredicatePathToken(Collection predicates) {
this.predicates = predicates;
}
@Override
public void evaluate(String currentPath, PathRef ref, Object model, EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
if (accept(model, ctx.rootDocument(), ctx.configuration(), ctx)) {
PathRef op = ctx.forUpdate() ? ref : PathRef.NO_OP;
if (isLeaf()) {
ctx.addResult(currentPath, op, model);
} else {
next().evaluate(currentPath, op, model, ctx);
}
}
} else if (ctx.jsonProvider().isArray(model)){
int idx = 0;
Iterable> objects = ctx.jsonProvider().toIterable(model);
for (Object idxModel : objects) {
if (accept(idxModel, ctx.rootDocument(), ctx.configuration(), ctx)) {
handleArrayIndex(idx, currentPath, model, ctx);
}
idx++;
}
} else {
if (isUpstreamDefinite()) {
throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model));
}
}
}
public boolean accept(final Object obj, final Object root, final Configuration configuration, EvaluationContextImpl evaluationContext) {
Predicate.PredicateContext ctx = new PredicateContextImpl(obj, root, configuration, evaluationContext.documentEvalCache());
for (Predicate predicate : predicates) {
try {
if (!predicate.apply(ctx)) {
return false;
}
} catch (InvalidPathException e) {
return false;
}
}
return true;
}
@Override
public String getPathFragment() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0; i < predicates.size(); i++){
if(i != 0){
sb.append(",");
}
sb.append("?");
}
sb.append("]");
return sb.toString();
}
@Override
public boolean isTokenDefinite() {
return false;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import java.util.ArrayList;
import java.util.List;
import static com.jayway.jsonpath.internal.Utils.onlyOneIsTrueNonThrow;
/**
*
*/
public class PropertyPathToken extends PathToken {
private final List properties;
private final String stringDelimiter;
public PropertyPathToken(List properties, char stringDelimiter) {
if (properties.isEmpty()) {
throw new InvalidPathException("Empty properties");
}
this.properties = properties;
this.stringDelimiter = Character.toString(stringDelimiter);
}
public List getProperties() {
return properties;
}
public boolean singlePropertyCase() {
return properties.size() == 1;
}
public boolean multiPropertyMergeCase() {
return isLeaf() && properties.size() > 1;
}
public boolean multiPropertyIterationCase() {
// Semantics of this case is the same as semantics of ArrayPathToken with INDEX_SEQUENCE operation.
return !isLeaf() && properties.size() > 1;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
// Can't assert it in ctor because isLeaf() could be changed later on.
assert onlyOneIsTrueNonThrow(singlePropertyCase(), multiPropertyMergeCase(), multiPropertyIterationCase());
if (!ctx.jsonProvider().isMap(model)) {
if (!isUpstreamDefinite()
|| ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)) {
return;
} else {
String m = model == null ? "null" : model.getClass().getName();
throw new PathNotFoundException(String.format(
"Expected to find an object with property %s in path %s but found '%s'. " +
"This is not a json object according to the JsonProvider: '%s'.",
getPathFragment(), currentPath, m, ctx.configuration().jsonProvider().getClass().getName()));
}
}
if (singlePropertyCase() || multiPropertyMergeCase()) {
handleObjectProperty(currentPath, model, ctx, properties);
return;
}
assert multiPropertyIterationCase();
final List currentlyHandledProperty = new ArrayList(1);
currentlyHandledProperty.add(null);
for (final String property : properties) {
currentlyHandledProperty.set(0, property);
handleObjectProperty(currentPath, model, ctx, currentlyHandledProperty);
}
}
@Override
public boolean isTokenDefinite() {
// in case of leaf multiprops will be merged, so it's kinda definite
return singlePropertyCase() || multiPropertyMergeCase();
}
@Override
public String getPathFragment() {
return new StringBuilder()
.append("[")
.append(Utils.join(",", stringDelimiter, properties))
.append("]").toString();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.PathRef;
/**
*
*/
public class RootPathToken extends PathToken {
private PathToken tail;
private int tokenCount;
private final String rootToken;
RootPathToken(char rootToken) {
this.rootToken = Character.toString(rootToken);
this.tail = this;
this.tokenCount = 1;
}
public PathToken getTail(){
return this.tail;
}
@Override
public int getTokenCount() {
return tokenCount;
}
public RootPathToken append(PathToken next) {
this.tail = tail.appendTailToken(next);
this.tokenCount++;
return this;
}
public PathTokenAppender getPathTokenAppender(){
return new PathTokenAppender(){
@Override
public PathTokenAppender appendPathToken(PathToken next) {
append(next);
return this;
}
};
}
@Override
public void evaluate(String currentPath, PathRef pathRef, Object model, EvaluationContextImpl ctx) {
if (isLeaf()) {
PathRef op = ctx.forUpdate() ? pathRef : PathRef.NO_OP;
ctx.addResult(rootToken, op, model);
} else {
next().evaluate(rootToken, pathRef, model, ctx);
}
}
@Override
public String getPathFragment() {
return rootToken;
}
@Override
public boolean isTokenDefinite() {
return true;
}
public boolean isFunctionPath() {
return (tail instanceof FunctionPathToken);
}
public void setTail(PathToken token) {
this.tail = token;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/ScanPathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.Collection;
/**
*
*/
public class ScanPathToken extends PathToken {
ScanPathToken() {
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
PathToken pt = next();
walk(pt, currentPath, parent, model, ctx, createScanPredicate(pt, ctx));
}
public static void walk(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, Predicate predicate) {
if (ctx.jsonProvider().isMap(model)) {
walkObject(pt, currentPath, parent, model, ctx, predicate);
} else if (ctx.jsonProvider().isArray(model)) {
walkArray(pt, currentPath, parent, model, ctx, predicate);
}
}
public static void walkArray(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, Predicate predicate) {
if (predicate.matches(model)) {
if (pt.isLeaf()) {
pt.evaluate(currentPath, parent, model, ctx);
} else {
PathToken next = pt.next();
Iterable> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
next.setUpstreamArrayIndex(idx);
next.evaluate(evalPath, parent, evalModel, ctx);
idx++;
}
}
}
Iterable> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
walk(pt, evalPath, PathRef.create(model, idx), evalModel, ctx, predicate);
idx++;
}
}
public static void walkObject(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, Predicate predicate) {
if (predicate.matches(model)) {
pt.evaluate(currentPath, parent, model, ctx);
}
Collection properties = ctx.jsonProvider().getPropertyKeys(model);
for (String property : properties) {
String evalPath = currentPath + "['" + property + "']";
Object propertyModel = ctx.jsonProvider().getMapValue(model, property);
if (propertyModel != JsonProvider.UNDEFINED) {
walk(pt, evalPath, PathRef.create(model, property), propertyModel, ctx, predicate);
}
}
}
private static Predicate createScanPredicate(final PathToken target, final EvaluationContextImpl ctx) {
if (target instanceof PropertyPathToken) {
return new PropertyPathTokenPredicate(target, ctx);
} else if (target instanceof ArrayPathToken) {
return new ArrayPathTokenPredicate(ctx);
} else if (target instanceof WildcardPathToken) {
return new WildcardPathTokenPredicate();
} else if (target instanceof PredicatePathToken) {
return new FilterPathTokenPredicate(target, ctx);
} else {
return FALSE_PREDICATE;
}
}
@Override
public boolean isTokenDefinite() {
return false;
}
@Override
public String getPathFragment() {
return "..";
}
private interface Predicate {
boolean matches(Object model);
}
private static final Predicate FALSE_PREDICATE = new Predicate() {
@Override
public boolean matches(Object model) {
return false;
}
};
private static final class FilterPathTokenPredicate implements Predicate {
private final EvaluationContextImpl ctx;
private PredicatePathToken predicatePathToken;
private FilterPathTokenPredicate(PathToken target, EvaluationContextImpl ctx) {
this.ctx = ctx;
predicatePathToken = (PredicatePathToken) target;
}
@Override
public boolean matches(Object model) {
return predicatePathToken.accept(model, ctx.rootDocument(), ctx.configuration(), ctx);
}
}
private static final class WildcardPathTokenPredicate implements Predicate {
@Override
public boolean matches(Object model) {
return true;
}
}
private static final class ArrayPathTokenPredicate implements Predicate {
private final EvaluationContextImpl ctx;
private ArrayPathTokenPredicate(EvaluationContextImpl ctx) {
this.ctx = ctx;
}
@Override
public boolean matches(Object model) {
return ctx.jsonProvider().isArray(model);
}
}
private static final class PropertyPathTokenPredicate implements Predicate {
private final EvaluationContextImpl ctx;
private PropertyPathToken propertyPathToken;
private PropertyPathTokenPredicate(PathToken target, EvaluationContextImpl ctx) {
this.ctx = ctx;
propertyPathToken = (PropertyPathToken) target;
}
@Override
public boolean matches(Object model) {
if (! ctx.jsonProvider().isMap(model)) {
return false;
}
//
// The commented code below makes it really hard understand, use and predict the result
// of deep scanning operations. It might be correct but was decided to be
// left out until the behavior of REQUIRE_PROPERTIES is more strictly defined
// in a deep scanning scenario. For details read conversation in commit
// https://github.com/jayway/JsonPath/commit/1a72fc078deb16995e323442bfb681bd715ce45a#commitcomment-14616092
//
// if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
// // Have to require properties defined in path when an indefinite path is evaluated,
// // so have to go there and search for it.
// return true;
// }
if (! propertyPathToken.isTokenDefinite()) {
// It's responsibility of PropertyPathToken code to handle indefinite scenario of properties,
// so we'll allow it to do its job.
return true;
}
if (propertyPathToken.isLeaf() && ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) {
// In case of DEFAULT_PATH_LEAF_TO_NULL missing properties is not a problem.
return true;
}
Collection keys = ctx.jsonProvider().getPropertyKeys(model);
return keys.containsAll(propertyPathToken.getProperties());
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
import java.util.Collections;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import static java.util.Arrays.asList;
/**
*
*/
public class WildcardPathToken extends PathToken {
public WildcardPathToken() {
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
for (String property : ctx.jsonProvider().getPropertyKeys(model)) {
handleObjectProperty(currentPath, model, ctx, Collections.singletonList(property));
}
} else if (ctx.jsonProvider().isArray(model)) {
for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) {
try {
handleArrayIndex(idx, currentPath, model, ctx);
} catch (PathNotFoundException p){
if(ctx.options().contains(Option.REQUIRE_PROPERTIES)){
throw p;
}
}
}
}
}
@Override
public boolean isTokenDefinite() {
return false;
}
@Override
public String getPathFragment() {
return "[*]";
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/cache/Cache.java
================================================
package com.jayway.jsonpath.spi.cache;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.JsonPath;
public interface Cache {
/**
* Get the Cached JsonPath
* @param key cache key to lookup the JsonPath
* @return JsonPath
*/
JsonPath get(String key);
/**
* Add JsonPath to the cache
* @param key cache key to store the JsonPath
* @param value JsonPath to be cached
* @throws InvalidJsonException
*/
void put(String key, JsonPath value);
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java
================================================
package com.jayway.jsonpath.spi.cache;
import com.jayway.jsonpath.JsonPathException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import static com.jayway.jsonpath.internal.Utils.notNull;
public class CacheProvider {
private static final AtomicReferenceFieldUpdater UPDATER =
AtomicReferenceFieldUpdater.newUpdater(CacheProvider.class, Cache.class, "cache");
private static final CacheProvider instance = new CacheProvider();
private volatile Cache cache;
private static class CacheHolder {
static final Cache CACHE;
static {
Cache cache = CacheProvider.instance.cache;
// the application is trying to use the cache
// and if no external implementation has been registered,
// we need to initialise it to the default LRUCache
if (cache == null) {
cache = getDefaultCache();
// on the off chance that the cache implementation was registered during
// initialisation of the holder, this should be respected, so if the
// default cache can't be written back, just read the user supplied value again
if (!UPDATER.compareAndSet(instance, null, cache)) {
cache = CacheProvider.instance.cache;
}
}
CACHE = cache;
}
}
public static void setCache(Cache cache){
notNull(cache, "Cache may not be null");
if (!UPDATER.compareAndSet(instance, null, cache)) {
throw new JsonPathException("Cache provider must be configured before cache is accessed and must not be registered twice.");
}
}
public static Cache getCache() {
return CacheHolder.CACHE;
}
private static Cache getDefaultCache(){
return new LRUCache(400);
//return new NOOPCache();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/cache/LRUCache.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.spi.cache;
import com.jayway.jsonpath.JsonPath;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
public class LRUCache implements Cache {
private final ReentrantLock lock = new ReentrantLock();
private final Map map = new ConcurrentHashMap();
private final Deque queue = new LinkedList();
private final int limit;
public LRUCache(int limit) {
this.limit = limit;
}
public void put(String key, JsonPath value) {
JsonPath oldValue = map.put(key, value);
if (oldValue != null) {
removeThenAddKey(key);
} else {
addKey(key);
}
if (map.size() > limit) {
map.remove(removeLast());
}
}
public JsonPath get(String key) {
JsonPath jsonPath = map.get(key);
if(jsonPath != null){
removeThenAddKey(key);
}
return jsonPath;
}
private void addKey(String key) {
lock.lock();
try {
queue.addFirst(key);
} finally {
lock.unlock();
}
}
private String removeLast() {
lock.lock();
try {
final String removedKey = queue.removeLast();
return removedKey;
} finally {
lock.unlock();
}
}
private void removeThenAddKey(String key) {
lock.lock();
try {
queue.removeFirstOccurrence(key);
queue.addFirst(key);
} finally {
lock.unlock();
}
}
private void removeFirstOccurrence(String key) {
lock.lock();
try {
queue.removeFirstOccurrence(key);
} finally {
lock.unlock();
}
}
public JsonPath getSilent(String key) {
return map.get(key);
}
public void remove(String key) {
removeFirstOccurrence(key);
map.remove(key);
}
public int size() {
return map.size();
}
public String toString() {
return map.toString();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/cache/NOOPCache.java
================================================
package com.jayway.jsonpath.spi.cache;
import com.jayway.jsonpath.JsonPath;
public class NOOPCache implements Cache {
@Override
public JsonPath get(String key) {
return null;
}
@Override
public void put(String key, JsonPath value) {
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.spi.json;
import com.jayway.jsonpath.JsonPathException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public abstract class AbstractJsonProvider implements JsonProvider {
/**
* checks if object is an array
*
* @param obj object to check
* @return true if obj is an array
*/
public boolean isArray(Object obj) {
return (obj instanceof List);
}
/**
* Extracts a value from an array
*
* @param obj an array
* @param idx index
* @return the entry at the given index
*/
public Object getArrayIndex(Object obj, int idx) {
return ((List) obj).get(idx);
}
@Deprecated
public final Object getArrayIndex(Object obj, int idx, boolean unwrap){
return getArrayIndex(obj, idx);
}
public void setArrayIndex(Object array, int index, Object newValue) {
if (!isArray(array)) {
throw new UnsupportedOperationException();
} else {
List l = (List) array;
if (index == l.size()){
l.add(newValue);
}else {
l.set(index, newValue);
}
}
}
/**
* Extracts a value from an map
*
* @param obj a map
* @param key property key
* @return the map entry or {@link com.jayway.jsonpath.spi.json.JsonProvider#UNDEFINED} for missing properties
*/
public Object getMapValue(Object obj, String key){
Map m = (Map) obj;
if(!m.containsKey(key)){
return JsonProvider.UNDEFINED;
} else {
return m.get(key);
}
}
/**
* Sets a value in an object
*
* @param obj an object
* @param key a String key
* @param value the value to set
*/
@SuppressWarnings("unchecked")
public void setProperty(Object obj, Object key, Object value) {
if (isMap(obj))
((Map) obj).put(key.toString(), value);
else {
throw new JsonPathException("setProperty operation cannot be used with " + obj!=null?obj.getClass().getName():"null");
}
}
/**
* Removes a value in an object or array
*
* @param obj an array or an object
* @param key a String key or a numerical index to remove
*/
@SuppressWarnings("unchecked")
public void removeProperty(Object obj, Object key) {
if (isMap(obj))
((Map) obj).remove(key.toString());
else {
List list = (List) obj;
int index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
list.remove(index);
}
}
/**
* checks if object is a map (i.e. no array)
*
* @param obj object to check
* @return true if the object is a map
*/
public boolean isMap(Object obj) {
return (obj instanceof Map);
}
/**
* Returns the keys from the given object
*
* @param obj an object
* @return the keys for an object
*/
@SuppressWarnings("unchecked")
public Collection getPropertyKeys(Object obj) {
if (isArray(obj)) {
throw new UnsupportedOperationException();
} else {
return ((Map) obj).keySet();
}
}
/**
* Get the length of an array or object
*
* @param obj an array or an object
* @return the number of entries in the array or object
*/
public int length(Object obj) {
if (isArray(obj)) {
return ((List) obj).size();
} else if (isMap(obj)){
return getPropertyKeys(obj).size();
} else if(obj instanceof String){
return ((String)obj).length();
}
throw new JsonPathException("length operation cannot be applied to " + (obj != null ? obj.getClass().getName()
: "null"));
}
/**
* Converts given array to an {@link Iterable}
*
* @param obj an array
* @return an Iterable that iterates over the entries of an array
*/
@SuppressWarnings("unchecked")
public Iterable> toIterable(Object obj) {
if (isArray(obj))
return ((Iterable) obj);
else
throw new JsonPathException("Cannot iterate over " + obj!=null?obj.getClass().getName():"null");
}
@Override
public Object unwrap(Object obj) {
return obj;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.spi.json;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.JsonPathException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class GsonJsonProvider extends AbstractJsonProvider {
private static final JsonParser PARSER = new JsonParser();
private final Gson gson;
/**
* Initializes the {@code GsonJsonProvider} using the default {@link Gson} object.
*/
public GsonJsonProvider() {
this(new Gson());
}
/**
* Initializes the {@code GsonJsonProvider} using a customized {@link Gson} object.
*
* @param gson the customized Gson object.
*/
public GsonJsonProvider(final Gson gson) {
this.gson = gson;
}
public Object unwrap(final Object o) {
if (o == null) {
return null;
}
if (!(o instanceof JsonElement)) {
return o;
}
JsonElement e = (JsonElement) o;
if (e.isJsonNull()) {
return null;
} else if (e.isJsonPrimitive()) {
JsonPrimitive p = e.getAsJsonPrimitive();
if (p.isString()) {
return p.getAsString();
} else if (p.isBoolean()) {
return p.getAsBoolean();
} else if (p.isNumber()) {
return unwrapNumber(p.getAsNumber());
}
}
return o;
}
private static boolean isPrimitiveNumber(final Number n) {
return n instanceof Integer ||
n instanceof Float ||
n instanceof Double ||
n instanceof Long ||
n instanceof BigDecimal ||
n instanceof BigInteger;
}
private static Number unwrapNumber(final Number n) {
Number unwrapped;
if (!isPrimitiveNumber(n)) {
BigDecimal bigDecimal = new BigDecimal(n.toString());
if (bigDecimal.scale() <= 0) {
if (bigDecimal.abs().compareTo(new BigDecimal(Integer.MAX_VALUE)) <= 0) {
unwrapped = bigDecimal.intValue();
} else if (bigDecimal.abs().compareTo(new BigDecimal(Long.MAX_VALUE)) <= 0){
unwrapped = bigDecimal.longValue();
} else {
unwrapped = bigDecimal;
}
} else {
final double doubleValue = bigDecimal.doubleValue();
if (BigDecimal.valueOf(doubleValue).compareTo(bigDecimal) != 0) {
unwrapped = bigDecimal;
} else {
unwrapped = doubleValue;
}
}
} else {
unwrapped = n;
}
return unwrapped;
}
@Override
public Object parse(final String json) throws InvalidJsonException {
return PARSER.parse(json);
}
@Override
public Object parse(final InputStream jsonStream, final String charset) throws InvalidJsonException {
try {
return PARSER.parse(new InputStreamReader(jsonStream, charset));
} catch (UnsupportedEncodingException e) {
throw new JsonPathException(e);
}
}
@Override
public String toJson(final Object obj) {
return gson.toJson(obj);
}
@Override
public Object createArray() {
return new JsonArray();
}
@Override
public Object createMap() {
return new JsonObject();
}
@Override
public boolean isArray(final Object obj) {
return (obj instanceof JsonArray || obj instanceof List);
}
@Override
public Object getArrayIndex(final Object obj, final int idx) {
return toJsonArray(obj).get(idx);
}
@Override
public void setArrayIndex(final Object array, final int index, final Object newValue) {
if (!isArray(array)) {
throw new UnsupportedOperationException();
} else {
JsonArray arr = toJsonArray(array);
if (index == arr.size()) {
arr.add(createJsonElement(newValue));
} else {
arr.set(index, createJsonElement(newValue));
}
}
}
@Override
public Object getMapValue(final Object obj, final String key) {
JsonObject jsonObject = toJsonObject(obj);
Object o = jsonObject.get(key);
if (!jsonObject.has(key)) {
return UNDEFINED;
} else {
return unwrap(o);
}
}
@Override
public void setProperty(final Object obj, final Object key, final Object value) {
if (isMap(obj)) {
toJsonObject(obj).add(key.toString(), createJsonElement(value));
} else {
JsonArray array = toJsonArray(obj);
int index;
if (key != null) {
index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
} else {
index = array.size();
}
if (index == array.size()) {
array.add(createJsonElement(value));
} else {
array.set(index, createJsonElement(value));
}
}
}
@SuppressWarnings("unchecked")
public void removeProperty(final Object obj, final Object key) {
if (isMap(obj)) {
toJsonObject(obj).remove(key.toString());
} else {
JsonArray array = toJsonArray(obj);
int index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
array.remove(index);
}
}
@Override
public boolean isMap(final Object obj) {
// return (obj instanceof JsonObject || obj instanceof Map);
return (obj instanceof JsonObject);
}
@Override
public Collection getPropertyKeys(final Object obj) {
List keys = new ArrayList();
for (Map.Entry entry : toJsonObject(obj).entrySet()) {
keys.add(entry.getKey());
}
return keys;
}
@Override
public int length(final Object obj) {
if (isArray(obj)) {
return toJsonArray(obj).size();
} else if (isMap(obj)) {
return toJsonObject(obj).entrySet().size();
} else {
if (obj instanceof JsonElement) {
JsonElement element = toJsonElement(obj);
if (element.isJsonPrimitive()) {
return element.toString().length();
}
}
}
throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName()
: "null"));
}
@Override
public Iterable> toIterable(final Object obj) {
JsonArray arr = toJsonArray(obj);
List values = new ArrayList(arr.size());
for (Object o : arr) {
values.add(unwrap(o));
}
return values;
}
private JsonElement createJsonElement(final Object o) {
return gson.toJsonTree(o);
}
private JsonArray toJsonArray(final Object o) {
return (JsonArray) o;
}
private JsonObject toJsonObject(final Object o) {
return (JsonObject) o;
}
private JsonElement toJsonElement(final Object o) {
return (JsonElement) o;
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonNodeJsonProvider.java
================================================
package com.jayway.jsonpath.spi.json;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.JsonPathException;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import tools.jackson.databind.node.StringNode;
public class Jackson3JsonNodeJsonProvider extends AbstractJsonProvider {
private static final ObjectMapper defaultObjectMapper = new ObjectMapper();
protected ObjectMapper objectMapper;
public ObjectMapper getObjectMapper() {
return objectMapper;
}
/**
* Initialize the JacksonJsonNodeJsonProvider with the default ObjectMapper and ObjectReader
*/
public Jackson3JsonNodeJsonProvider() {
this(defaultObjectMapper);
}
/**
* Initialize the JacksonJsonNodeJsonProvider with a custom ObjectMapper and ObjectReader.
*
* @param objectMapper the ObjectMapper to use
*/
public Jackson3JsonNodeJsonProvider(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Object parse(String json) throws InvalidJsonException {
try {
return objectMapper.readTree(json);
} catch (JacksonException e) {
throw new InvalidJsonException(e, json);
}
}
@Override
public Object parse(byte[] json) throws InvalidJsonException {
try {
return objectMapper.readTree(json);
} catch (JacksonException e) {
throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8));
}
}
@Override
public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException {
try {
return objectMapper.readTree(new InputStreamReader(jsonStream, charset));
} catch (IOException e) {
throw new InvalidJsonException(e);
}
}
@Override
public String toJson(Object obj) {
if (!(obj instanceof JsonNode)) {
throw new JsonPathException("Not a JSON Node");
}
return obj.toString();
}
@Override
public Object createArray() {
return JsonNodeFactory.instance.arrayNode();
}
@Override
public Object createMap() {
return JsonNodeFactory.instance.objectNode();
}
public Object unwrap(Object o) {
if (o == null) {
return null;
}
if (!(o instanceof JsonNode)) {
return o;
}
JsonNode e = (JsonNode) o;
if (e.isValueNode()) {
if (e.isString()) {
return e.asString();
} else if (e.isBoolean()) {
return e.asBoolean();
} else if (e.isInt()) {
return e.asInt();
} else if (e.isLong()) {
return e.asLong();
} else if (e.isBigInteger()) {
return e.bigIntegerValue();
} else if (e.isDouble()) {
return e.doubleValue();
} else if (e.isFloat()) {
return e.floatValue();
} else if (e.isBigDecimal()) {
return e.decimalValue();
} else if (e.isNull()) {
return null;
}
}
return o;
}
@Override
public boolean isArray(Object obj) {
return (obj instanceof ArrayNode || obj instanceof List);
}
@Override
public Object getArrayIndex(Object obj, int idx) {
return toJsonArray(obj).get(idx);
}
@Override
public void setArrayIndex(Object array, int index, Object newValue) {
if (!isArray(array)) {
throw new UnsupportedOperationException();
} else {
ArrayNode arrayNode = toJsonArray(array);
if (index == arrayNode.size()) {
arrayNode.add(createJsonElement(newValue));
} else {
arrayNode.set(index, createJsonElement(newValue));
}
}
}
@Override
public Object getMapValue(Object obj, String key) {
ObjectNode jsonObject = toJsonObject(obj);
Object o = jsonObject.get(key);
if (!jsonObject.has(key)) {
return UNDEFINED;
} else {
return o;
}
}
@Override
public void setProperty(Object obj, Object key, Object value) {
// jlolling: Bug: #211 avoid create cloned nodes
if (isMap(obj)) {
setValueInObjectNode((ObjectNode) obj, key, value);
} else {
ArrayNode array = (ArrayNode) obj;
int index;
if (key != null) {
index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
} else {
index = array.size();
}
if (index == array.size()) {
array.add(createJsonElement(value));
} else {
array.set(index, createJsonElement(value));
}
}
}
public void removeProperty(Object obj, Object key) {
if (isMap(obj)) {
toJsonObject(obj).remove(key.toString());
} else {
ArrayNode array = toJsonArray(obj);
int index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
array.remove(index);
}
}
@Override
public boolean isMap(Object obj) {
return (obj instanceof ObjectNode);
}
@Override
public Collection getPropertyKeys(Object obj) {
return toJsonObject(obj).propertyNames();
}
@Override
public int length(Object obj) {
if (isArray(obj)) {
return toJsonArray(obj).size();
} else if (isMap(obj)) {
return toJsonObject(obj).size();
} else {
if (obj instanceof StringNode) {
StringNode element = (StringNode) obj;
return element.size();
}
}
throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName() : "null"));
}
@Override
public Iterable> toIterable(Object obj) {
ArrayNode arr = toJsonArray(obj);
Iterator> iterator = arr.iterator();
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Object next() {
return unwrap(iterator.next());
}
};
}
};
}
private JsonNode createJsonElement(Object o) {
if (o != null) {
// jlolling: avoid creating a cloned node: bug #211
if (o instanceof JsonNode) {
return (JsonNode) o;
} else {
return objectMapper.valueToTree(o);
}
} else {
return null;
}
}
private ArrayNode toJsonArray(Object o) {
return (ArrayNode) o;
}
private ObjectNode toJsonObject(Object o) {
return (ObjectNode) o;
}
private void setValueInObjectNode(ObjectNode objectNode, Object key, Object value) {
// jlolling: necessary to avoid deprecated methods and to avoid creating a cloned node. Bug: #211
if (value instanceof JsonNode) {
objectNode.set(key.toString(), (JsonNode) value);
} else if (value instanceof String) {
objectNode.put(key.toString(), (String) value);
} else if (value instanceof Integer) {
objectNode.put(key.toString(), (Integer) value);
} else if (value instanceof Long) {
objectNode.put(key.toString(), (Long) value);
} else if (value instanceof Short) {
objectNode.put(key.toString(), (Short) value);
} else if (value instanceof BigInteger) {
objectNode.put(key.toString(), (BigInteger) value);
} else if (value instanceof Double) {
objectNode.put(key.toString(), (Double) value);
} else if (value instanceof Float) {
objectNode.put(key.toString(), (Float) value);
} else if (value instanceof BigDecimal) {
objectNode.put(key.toString(), (BigDecimal) value);
} else if (value instanceof Boolean) {
objectNode.put(key.toString(), (Boolean) value);
} else if (value instanceof byte[]) {
objectNode.put(key.toString(), (byte[]) value);
} else if (value == null) {
objectNode.set(key.toString(), null); // this will create a null-node
} else {
objectNode.set(key.toString(), createJsonElement(value));
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/Jackson3JsonProvider.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.spi.json;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import com.jayway.jsonpath.InvalidJsonException;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.ObjectReader;
public class Jackson3JsonProvider extends AbstractJsonProvider {
private static final ObjectMapper defaultObjectMapper = new ObjectMapper();
private static final ObjectReader defaultObjectReader = defaultObjectMapper.reader().forType(Object.class);
protected ObjectMapper objectMapper;
protected ObjectReader objectReader;
public ObjectMapper getObjectMapper() {
return objectMapper;
}
/**
* Initialize the JacksonProvider with the default ObjectMapper and ObjectReader
*/
public Jackson3JsonProvider() {
this(defaultObjectMapper, defaultObjectReader);
}
/**
* Initialize the JacksonProvider with a custom ObjectMapper.
*
* @param objectMapper the ObjectMapper to use
*/
public Jackson3JsonProvider(ObjectMapper objectMapper) {
this(objectMapper, objectMapper.reader().forType(Object.class));
}
/**
* Initialize the JacksonProvider with a custom ObjectMapper and ObjectReader.
*
* @param objectMapper the ObjectMapper to use
* @param objectReader the ObjectReader to use
*/
public Jackson3JsonProvider(ObjectMapper objectMapper, ObjectReader objectReader) {
this.objectMapper = objectMapper;
this.objectReader = objectReader;
}
@Override
public Object parse(String json) throws InvalidJsonException {
try {
return objectReader.readValue(json);
} catch (JacksonException e) {
throw new InvalidJsonException(e, json);
}
}
@Override
public Object parse(byte[] json) throws InvalidJsonException {
try {
return objectReader.readValue(json);
} catch (JacksonException e) {
throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8));
}
}
@Override
public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException {
try {
return objectReader.readValue(new InputStreamReader(jsonStream, charset));
} catch (IOException e) {
throw new InvalidJsonException(e);
}
}
@Override
public String toJson(Object obj) {
StringWriter writer = new StringWriter();
try {
objectMapper.writeValue(writer, obj);
writer.flush();
writer.close();
return writer.getBuffer().toString();
} catch (IOException e) {
throw new InvalidJsonException(e);
}
}
@Override
public List createArray() {
return new LinkedList();
}
@Override
public Object createMap() {
return new LinkedHashMap();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonNodeJsonProvider.java
================================================
package com.jayway.jsonpath.spi.json;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.JsonPathException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class JacksonJsonNodeJsonProvider extends AbstractJsonProvider {
private static final ObjectMapper defaultObjectMapper = new ObjectMapper();
protected ObjectMapper objectMapper;
public ObjectMapper getObjectMapper() {
return objectMapper;
}
/**
* Initialize the JacksonJsonNodeJsonProvider with the default ObjectMapper and ObjectReader
*/
public JacksonJsonNodeJsonProvider() {
this(defaultObjectMapper);
}
/**
* Initialize the JacksonJsonNodeJsonProvider with a custom ObjectMapper and ObjectReader.
*
* @param objectMapper the ObjectMapper to use
*/
public JacksonJsonNodeJsonProvider(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Object parse(String json) throws InvalidJsonException {
try {
return objectMapper.readTree(json);
} catch (IOException e) {
throw new InvalidJsonException(e, json);
}
}
@Override
public Object parse(byte[] json)
throws InvalidJsonException {
try {
return objectMapper.readTree(json);
} catch (IOException e) {
throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8));
}
}
@Override
public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException {
try {
return objectMapper.readTree(new InputStreamReader(jsonStream, charset));
} catch (IOException e) {
throw new InvalidJsonException(e);
}
}
@Override
public String toJson(Object obj) {
if (!(obj instanceof JsonNode)) {
throw new JsonPathException("Not a JSON Node");
}
return obj.toString();
}
@Override
public Object createArray() {
return JsonNodeFactory.instance.arrayNode();
}
@Override
public Object createMap() {
return JsonNodeFactory.instance.objectNode();
}
public Object unwrap(Object o) {
if (o == null) {
return null;
}
if (!(o instanceof JsonNode)) {
return o;
}
JsonNode e = (JsonNode) o;
if (e.isValueNode()) {
if (e.isTextual()) {
return e.asText();
} else if (e.isBoolean()) {
return e.asBoolean();
} else if (e.isInt()) {
return e.asInt();
} else if (e.isLong()) {
return e.asLong();
} else if (e.isBigInteger()) {
return e.bigIntegerValue();
} else if (e.isDouble()) {
return e.doubleValue();
} else if (e.isFloat()) {
return e.floatValue();
} else if (e.isBigDecimal()) {
return e.decimalValue();
} else if (e.isNull()) {
return null;
}
}
return o;
}
@Override
public boolean isArray(Object obj) {
return (obj instanceof ArrayNode || obj instanceof List);
}
@Override
public Object getArrayIndex(Object obj, int idx) {
return toJsonArray(obj).get(idx);
}
@Override
public void setArrayIndex(Object array, int index, Object newValue) {
if (!isArray(array)) {
throw new UnsupportedOperationException();
} else {
ArrayNode arrayNode = toJsonArray(array);
if (index == arrayNode.size()){
arrayNode.add(createJsonElement(newValue));
}else {
arrayNode.set(index, createJsonElement(newValue));
}
}
}
@Override
public Object getMapValue(Object obj, String key) {
ObjectNode jsonObject = toJsonObject(obj);
Object o = jsonObject.get(key);
if (!jsonObject.has(key)) {
return UNDEFINED;
} else {
return o;
}
}
@Override
public void setProperty(Object obj, Object key, Object value) {
// jlolling: Bug: #211 avoid create cloned nodes
if (isMap(obj)) {
setValueInObjectNode((ObjectNode) obj, key, value);
} else {
ArrayNode array = (ArrayNode) obj;
int index;
if (key != null) {
index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
} else {
index = array.size();
}
if (index == array.size()) {
array.add(createJsonElement(value));
} else {
array.set(index, createJsonElement(value));
}
}
}
public void removeProperty(Object obj, Object key) {
if (isMap(obj)) {
toJsonObject(obj).remove(key.toString());
} else {
ArrayNode array = toJsonArray(obj);
int index = key instanceof Integer ? (Integer) key : Integer.parseInt(key.toString());
array.remove(index);
}
}
@Override
public boolean isMap(Object obj) {
return (obj instanceof ObjectNode);
}
@Override
public Collection getPropertyKeys(Object obj) {
List keys = new ArrayList();
Iterator iter = toJsonObject(obj).fieldNames();
while (iter.hasNext()){
keys.add(iter.next());
}
return keys;
}
@Override
public int length(Object obj) {
if (isArray(obj)) {
return toJsonArray(obj).size();
} else if (isMap(obj)) {
return toJsonObject(obj).size();
} else {
if (obj instanceof TextNode) {
TextNode element = (TextNode) obj;
return element.size();
}
}
throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName()
: "null"));
}
@Override
public Iterable> toIterable(Object obj) {
ArrayNode arr = toJsonArray(obj);
Iterator> iterator = arr.iterator();
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Object next() {
return unwrap(iterator.next());
}
};
}
};
}
private JsonNode createJsonElement(Object o) {
if (o != null) {
// jlolling: avoid creating a cloned node: bug #211
if (o instanceof JsonNode) {
return (JsonNode) o;
} else {
return objectMapper.valueToTree(o);
}
} else {
return null;
}
}
private ArrayNode toJsonArray(Object o) {
return (ArrayNode) o;
}
private ObjectNode toJsonObject(Object o) {
return (ObjectNode) o;
}
private void setValueInObjectNode(ObjectNode objectNode, Object key, Object value) {
// jlolling: necessary to avoid deprecated methods and to avoid creating a cloned node. Bug: #211
if (value instanceof JsonNode) {
objectNode.set(key.toString(), (JsonNode) value);
} else if (value instanceof String) {
objectNode.put(key.toString(), (String) value);
} else if (value instanceof Integer) {
objectNode.put(key.toString(), (Integer) value);
} else if (value instanceof Long) {
objectNode.put(key.toString(), (Long) value);
} else if (value instanceof Short) {
objectNode.put(key.toString(), (Short) value);
} else if (value instanceof BigInteger) {
objectNode.put(key.toString(), (BigInteger) value);
} else if (value instanceof Double) {
objectNode.put(key.toString(), (Double) value);
} else if (value instanceof Float) {
objectNode.put(key.toString(), (Float) value);
} else if (value instanceof BigDecimal) {
objectNode.put(key.toString(), (BigDecimal) value);
} else if (value instanceof Boolean) {
objectNode.put(key.toString(), (Boolean) value);
} else if (value instanceof byte[]) {
objectNode.put(key.toString(), (byte[]) value);
} else if (value == null) {
objectNode.set(key.toString(), null); // this will create a null-node
} else {
objectNode.set(key.toString(), createJsonElement(value));
}
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java
================================================
/*
* Copyright 2011 the original author or authors.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.spi.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.jayway.jsonpath.InvalidJsonException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
public class JacksonJsonProvider extends AbstractJsonProvider {
private static final ObjectMapper defaultObjectMapper = new ObjectMapper();
private static final ObjectReader defaultObjectReader = defaultObjectMapper.reader().forType(Object.class);
protected ObjectMapper objectMapper;
protected ObjectReader objectReader;
public ObjectMapper getObjectMapper() {
return objectMapper;
}
/**
* Initialize the JacksonProvider with the default ObjectMapper and ObjectReader
*/
public JacksonJsonProvider() {
this(defaultObjectMapper, defaultObjectReader);
}
/**
* Initialize the JacksonProvider with a custom ObjectMapper.
* @param objectMapper the ObjectMapper to use
*/
public JacksonJsonProvider(ObjectMapper objectMapper) {
this(objectMapper, objectMapper.reader().forType(Object.class));
}
/**
* Initialize the JacksonProvider with a custom ObjectMapper and ObjectReader.
* @param objectMapper the ObjectMapper to use
* @param objectReader the ObjectReader to use
*/
public JacksonJsonProvider(ObjectMapper objectMapper, ObjectReader objectReader) {
this.objectMapper = objectMapper;
this.objectReader = objectReader;
}
@Override
public Object parse(String json) throws InvalidJsonException {
try {
return objectReader.readValue(json);
} catch (IOException e) {
throw new InvalidJsonException(e, json);
}
}
@Override
public Object parse(byte[] json)
throws InvalidJsonException {
try {
return objectReader.readValue(json);
} catch (IOException e) {
throw new InvalidJsonException(e, new String(json, StandardCharsets.UTF_8));
}
}
@Override
public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException {
try {
return objectReader.readValue(new InputStreamReader(jsonStream, charset));
} catch (IOException e) {
throw new InvalidJsonException(e);
}
}
@Override
public String toJson(Object obj) {
StringWriter writer = new StringWriter();
try {
JsonGenerator generator = objectMapper.getFactory().createGenerator(writer);
objectMapper.writeValue(generator, obj);
writer.flush();
writer.close();
generator.close();
return writer.getBuffer().toString();
} catch (IOException e) {
throw new InvalidJsonException(e);
}
}
@Override
public List createArray() {
return new LinkedList();
}
@Override
public Object createMap() {
return new LinkedHashMap();
}
}
================================================
FILE: json-path/src/main/java/com/jayway/jsonpath/spi/json/JakartaJsonProvider.java
================================================
package com.jayway.jsonpath.spi.json;
import com.jayway.jsonpath.InvalidJsonException;
import com.jayway.jsonpath.JsonPathException;
import jakarta.json.JsonArray;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonNumber;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonReader;
import jakarta.json.JsonString;
import jakarta.json.JsonStructure;
import jakarta.json.JsonValue;
import jakarta.json.spi.JsonProvider;
import jakarta.json.stream.JsonParsingException;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
public class JakartaJsonProvider extends AbstractJsonProvider {
private static final JsonProvider defaultJsonProvider = JsonProvider.provider();
private static final JsonBuilderFactory jsonBuilderFactory = defaultJsonProvider.createBuilderFactory(null);
private final boolean mutableJson;
/**
* Constructs new instance of parsing and serialization adapter for Jakarta EE 9
* JSON-P default provider. JSON files, strings, and streams can be loaded, parsed,
* and navigated with JsonPath expressions, and values retrieved - but no changes
* to the loaded JSON document are permitted, and will yield exceptions.
*/
public JakartaJsonProvider() {
this.mutableJson = false;
}
/**
* Constructs new instance of parsing and serialization adapter for Jakarta EE 9
* JSON-P default provider, and optionally enables proxying of {@code JsonObject}
* and {@code JsonArray} entities to implement mutable JSON structures. By default,
* all structures and values produced and consumed by JSON-P are immutable. This
* comes at an extra cost to perfomance and memory consumption, so enable only if
* expected use cases include add/put/replace/delete operations on JSON document.
*
* @param mutableJson enable dynamic proxies for JSON structures
*/
public JakartaJsonProvider(boolean mutableJson) {
this.mutableJson = mutableJson;
}
@Override
public Object parse(String json) throws InvalidJsonException {
return parse(new StringReader(json));
}
@Override
public Object parse(byte[] json)
throws InvalidJsonException {
return parse(new InputStreamReader(new ByteArrayInputStream(json), StandardCharsets.UTF_8));
}
@Override
public Object parse(InputStream jsonStream, String charset) throws InvalidJsonException {
try {
return parse(new InputStreamReader(jsonStream, charset));
} catch (UnsupportedEncodingException e) {
throw new JsonPathException(e);
}
}
private Object parse(Reader jsonInput) {
try (JsonReader jsonReader = defaultJsonProvider.createReader(jsonInput)) {
JsonStructure jsonStruct = jsonReader.read();
return mutableJson ? proxyAll(jsonStruct) : jsonStruct;
} catch (JsonParsingException e) {
throw new InvalidJsonException(e);
}
// not catching a JsonException as it never happens here
}
@Override
public String toJson(Object obj) {
if (obj instanceof JsonObjectBuilder) {
obj = ((JsonObjectBuilder) obj).build();
} else if (obj instanceof JsonArrayBuilder) {
obj = ((JsonArrayBuilder) obj).build();
} else if (obj instanceof List) {
obj = jsonBuilderFactory.createArrayBuilder((Collection>) obj).build();
}
return obj.toString();
}
@Override
public Object createArray() {
if (mutableJson) {
return new JsonArrayProxy(jsonBuilderFactory.createArrayBuilder().build());
} else {
return new LinkedList();
}
}
@Override
public Object createMap() {
if (mutableJson) {
return new JsonObjectProxy(jsonBuilderFactory.createObjectBuilder().build());
} else {
return jsonBuilderFactory.createObjectBuilder();
}
}
@Override
public boolean isArray(Object obj) {
return (obj instanceof JsonArray || obj instanceof JsonArrayBuilder || obj instanceof List);
}
@Override
public Object getArrayIndex(Object obj, int idx) {
if (obj instanceof JsonArrayBuilder) {
obj = ((JsonArrayBuilder) obj).build();
}
if (obj instanceof JsonArray) {
return ((JsonArray) obj).get(idx);
} else if (obj instanceof List>) {
return super.getArrayIndex(obj, idx);
} else {
throw new UnsupportedOperationException();
}
}
@Override
public void setArrayIndex(Object array, int index, Object newValue) {
if (array instanceof JsonArrayBuilder) {
// next line is not optimal, but ArrayBuilder has no size() method
if (index == ((JsonArrayBuilder) array).build().size()) {
array = ((JsonArrayBuilder) array).add(wrap(newValue));
} else {
array = ((JsonArrayBuilder) array).set(index, wrap(newValue));
}
} else if (array instanceof JsonArray) {
if (mutableJson && array instanceof JsonArrayProxy) {
((JsonArrayProxy) array).set(index, wrap(newValue));
} else {
throw new UnsupportedOperationException("JsonArray is immutable in JSON-P");
}
} else {
super.setArrayIndex(array, index, wrap(newValue));
}
}
@Override
public Object getMapValue(Object obj, String key) {
if (obj instanceof JsonObjectBuilder) {
obj = ((JsonObjectBuilder) obj).build();
}
if (obj instanceof JsonObject) {
JsonValue o = ((JsonObject) obj).get(key);
if (o == null) {
return UNDEFINED;
} else {
return unwrap(o);
}
} else {
throw new UnsupportedOperationException();
}
}
@Override
public void setProperty(Object obj, Object key, Object value) {
if (obj instanceof JsonObjectBuilder) {
((JsonObjectBuilder) obj).add(key.toString(), wrap(value));
} else if (mutableJson && obj instanceof JsonObjectProxy) {
((JsonObjectProxy) obj).put(key.toString(), wrap(value));
} else if (obj instanceof JsonObject) {
throw new UnsupportedOperationException("JsonObject is immutable in JSON-P");
} else if (obj instanceof JsonArrayBuilder) {
if (key == null) {
((JsonArrayBuilder) obj).add(wrap(value));
} else {
((JsonArrayBuilder) obj).set(toArrayIndex(key), wrap(value));
}
} else if (mutableJson && obj instanceof JsonArrayProxy) {
if (key == null) {
((JsonArrayProxy) obj).add(wrap(value));
} else {
((JsonArrayProxy) obj).set(toArrayIndex(key), wrap(value));
}
} else if (obj instanceof JsonArray) {
throw new UnsupportedOperationException("JsonArray is immutable in JSON-P");
} else if (obj instanceof List) {
@SuppressWarnings("unchecked")
List array = (List) obj;
if (key == null) {
array.add(wrap(value));
} else {
array.add(toArrayIndex(key), wrap(value));
}
} else {
throw new UnsupportedOperationException();
}
}
@SuppressWarnings("rawtypes")
public void removeProperty(Object obj, Object key) {
if (obj instanceof JsonObjectBuilder) {
((JsonObjectBuilder) obj).remove(key.toString());
} else if (obj instanceof JsonObject) {
if (mutableJson && obj instanceof JsonObjectProxy) {
((JsonObjectProxy) obj).remove(key);
} else {
throw new UnsupportedOperationException("JsonObject is immutable in JSON-P");
}
} else if (isArray(obj)) {
int index = toArrayIndex(key).intValue();
if (obj instanceof JsonArrayBuilder) {
((JsonArrayBuilder) obj).remove(index);
} else if (obj instanceof List) {
// this also covers JsonArray as it implements List<>
((List) obj).remove(index);
}
} else {
throw new UnsupportedOperationException();
}
}
@Override
public boolean isMap(Object obj) {
return (obj instanceof JsonObject || obj instanceof JsonObjectBuilder);
}
@Override
public Collection getPropertyKeys(Object obj) {
Set keys;
if (obj instanceof JsonObjectBuilder) {
keys = ((JsonObjectBuilder) obj).build().keySet();
} else if (obj instanceof JsonObject) {
keys = ((JsonObject) obj).keySet();
} else {
throw new UnsupportedOperationException("Json object is expected");
}
return new ArrayList(keys);
}
@Override
public int length(Object obj) {
if (isArray(obj)) {
if (obj instanceof JsonArrayBuilder) {
return ((JsonArrayBuilder) obj).build().size();
} else {
return ((List>) obj).size();
}
} else if (isMap(obj)) {
if (obj instanceof JsonObjectBuilder) {
obj = ((JsonObjectBuilder) obj).build();
}
return ((JsonObject) obj).size();
} else {
if (obj instanceof CharSequence) {
return ((CharSequence) obj).length();
}
}
String className = obj != null ? obj.getClass().getName() : null;
throw new JsonPathException("length operation can not applied to " + className);
}
@Override
public Iterable> toIterable(Object obj) {
List values;
if (isArray(obj)) {
if (obj instanceof JsonArrayBuilder) {
obj = ((JsonArrayBuilder) obj).build();
}
values = new ArrayList(((List>) obj).size());
for (Object val : ((List>) obj)) {
values.add(unwrap(val));
}
} else if (isMap(obj)) {
if (obj instanceof JsonObjectBuilder) {
obj = ((JsonObjectBuilder) obj).build();
}
values = new ArrayList(((JsonObject) obj).size());
for (JsonValue val : ((JsonObject) obj).values()) {
values.add(unwrap(val));
}
} else {
throw new UnsupportedOperationException("an array or object instance is expected");
}
return values;
}
@Override
public Object unwrap(Object obj) {
if (obj == null) {
return null;
}
if (!(obj instanceof JsonValue)) {
return obj;
}
switch (((JsonValue) obj).getValueType()) {
case ARRAY:
if (mutableJson && obj instanceof JsonArrayProxy) {
return (JsonArray) obj;
} else {
return ((JsonArray) obj).getValuesAs((JsonValue v) -> unwrap(v));
}
case STRING:
return ((JsonString) obj).getString();
case NUMBER:
if (((JsonNumber) obj).isIntegral()) {
//return ((JsonNumber) obj).bigIntegerValueExact();
try {
return ((JsonNumber) obj).intValueExact();
} catch (ArithmeticException e) {
return ((JsonNumber) obj).longValueExact();
}
} else {
//return ((JsonNumber) obj).bigDecimalValue();
return ((JsonNumber) obj).doubleValue();
}
case TRUE:
return Boolean.TRUE;
case FALSE:
return Boolean.FALSE;
case NULL:
return null;
default:
return obj;
}
}
private Integer toArrayIndex(Object index) {
try {
if (index instanceof Integer) {
return (Integer) index;
} else if (index instanceof Long) {
return Integer.valueOf(((Long) index).intValue());
} else if (index != null) {
return Integer.valueOf(index.toString());
} else {
//return null;
throw new IllegalArgumentException("Invalid array index");
}
} catch (NumberFormatException e) {
throw new JsonPathException(e);
}
}
private JsonValue wrap(Object obj) {
if (obj == null) {
return JsonValue.NULL;
} else if (obj instanceof JsonArray) {
if (!mutableJson || obj instanceof JsonArrayProxy) {
return (JsonArray) obj;
} else {
return proxyAll((JsonArray) obj);
}
} else if (obj instanceof JsonObject) {
if (!mutableJson || obj instanceof JsonObjectProxy) {
return (JsonObject) obj;
} else {
return proxyAll((JsonObject) obj);
}
} else if (obj instanceof JsonValue) {
return (JsonValue) obj;
} else if (Boolean.TRUE.equals(obj)) {
return JsonValue.TRUE;
} else if (Boolean.FALSE.equals(obj)) {
return JsonValue.FALSE;
} else if (obj instanceof CharSequence) {
return defaultJsonProvider.createValue(obj.toString());
} else if (obj instanceof Number) {
if (obj instanceof Integer) {
int v = ((Number) obj).intValue();
return defaultJsonProvider.createValue(v);
} else if (obj instanceof Long) {
long v = ((Number) obj).longValue();
return defaultJsonProvider.createValue(v);
} else if ((obj instanceof Float) || (obj instanceof Double)) {
double v = ((Number) obj).doubleValue();
return defaultJsonProvider.createValue(v);
} else if (obj instanceof BigInteger) {
return defaultJsonProvider.createValue((BigInteger) obj);
} else if (obj instanceof BigDecimal) {
return defaultJsonProvider.createValue((BigDecimal) obj);
} else {
// default to BigDecimal conversion for other numeric types
BigDecimal v = BigDecimal.valueOf(((Number) obj).doubleValue());
return defaultJsonProvider.createValue(v);
}
} else if (obj instanceof Collection) {
JsonArray result = jsonBuilderFactory.createArrayBuilder((Collection>) obj).build();
return mutableJson ? proxyAll(result) : result;
} else if (obj instanceof Map) {
@SuppressWarnings("unchecked")
Map map = (Map) obj;
JsonObject result = jsonBuilderFactory.createObjectBuilder(map).build();
return mutableJson ? proxyAll(result) : result;
} else if (obj instanceof JsonArrayBuilder) {
JsonArray result = ((JsonArrayBuilder) obj).build();
return mutableJson ? proxyAll(result) : result;
} else if (obj instanceof JsonObjectBuilder) {
JsonObject result = ((JsonObjectBuilder) obj).build();
return mutableJson ? proxyAll(result) : result;
} else {
String className = obj.getClass().getSimpleName();
throw new UnsupportedOperationException("Cannot create JSON element from " + className);
}
}
private JsonStructure proxyAll(JsonStructure jsonStruct) {
if (jsonStruct == null) {
return null;
} else if (jsonStruct instanceof JsonArrayProxy) {
return (JsonArray) jsonStruct;
} else if (jsonStruct instanceof JsonArray) {
List array = new ArrayList<>();
for (JsonValue v : (JsonArray) jsonStruct) {
if (v instanceof JsonStructure) {
v = proxyAll((JsonStructure) v);
}
array.add(v);
}
return new JsonArrayProxy(jsonBuilderFactory.createArrayBuilder(array).build());
} else if (jsonStruct instanceof JsonObjectProxy) {
return (JsonObject) jsonStruct;
} else if (jsonStruct instanceof JsonObject) {
Map map = new LinkedHashMap<>();
for (Map.Entry e : ((JsonObject) jsonStruct).entrySet()) {
JsonValue v = e.getValue();
if (v instanceof JsonStructure) {
v = proxyAll((JsonStructure) v);
}
map.put(e.getKey(), v);
}
return new JsonObjectProxy(jsonBuilderFactory.createObjectBuilder(map).build());
} else {
throw new IllegalArgumentException();
}
}
private static class JsonArrayProxy implements JsonArray {
private JsonArray arr;
JsonArrayProxy(JsonArray arr) {
this.arr = arr;
}
@Override
public JsonObject getJsonObject(int index) {
return arr.getJsonObject(index);
}
@Override
public JsonArray getJsonArray(int index) {
return arr.getJsonArray(index);
}
@Override
public JsonNumber getJsonNumber(int index) {
return arr.getJsonNumber(index);
}
@Override
public JsonString getJsonString(int index) {
return arr.getJsonString(index);
}
@Override
public List getValuesAs(Class clazz) {
return arr.getValuesAs(clazz);
}
@Override
public String getString(int index) {
return arr.getString(index);
}
@Override
public String getString(int index, String defaultValue) {
return arr.getString(index, defaultValue);
}
@Override
public int getInt(int index) {
return arr.getInt(index);
}
@Override
public int getInt(int index, int defaultValue) {
return arr.getInt(index, defaultValue);
}
@Override
public boolean getBoolean(int index) {
return arr.getBoolean(index);
}
@Override
public boolean getBoolean(int index, boolean defaultValue) {
return arr.getBoolean(index, defaultValue);
}
@Override
public boolean isNull(int index) {
return arr.isNull(index);
}
@Override
public ValueType getValueType() {
return arr.getValueType();
}
@Override
public int size() {
return arr.size();
}
@Override
public boolean isEmpty() {
return arr.isEmpty();
}
@Override
public boolean contains(Object o) {
return arr.contains(o);
}
@Override
public Iterator iterator() {
return new Iterator() {
final JsonArray refArr = arr;
final Iterator it = arr.iterator();
@Override
public boolean hasNext() {
if (refArr == arr) {
return it.hasNext();
} else {
throw new ConcurrentModificationException();
}
}
@Override
public JsonValue next() {
if (refArr == arr) {
return it.next();
} else {
throw new ConcurrentModificationException();
}
}
};
}
@Override
public Object[] toArray() {
return arr.toArray();
}
@Override
public T[] toArray(T[] a) {
return arr.toArray(a);
}
@Override
public boolean add(JsonValue e) {
arr = jsonBuilderFactory.createArrayBuilder(arr).add(e).build();
return true;
}
@Override
public boolean remove(Object o) {
int i = arr.indexOf(o);
if (i != -1) {
arr = jsonBuilderFactory.createArrayBuilder(arr).remove(i).build();
return true;
} else {
return false;
}
}
@Override
public boolean containsAll(Collection> c) {
return arr.containsAll(c);
}
@Override
public boolean addAll(Collection extends JsonValue> c) {
if (!c.isEmpty()) {
JsonArrayBuilder builder = jsonBuilderFactory.createArrayBuilder(arr);
for (JsonValue v : c) {
builder.add(v);
}
arr = builder.build();
return true;
} else {
return false;
}
}
@Override
public boolean addAll(int index, Collection extends JsonValue> c) {
if (c.isEmpty()) {
return false;
}
if (index < 0 || index >= arr.size()) {
throw new IndexOutOfBoundsException();
}
JsonArrayBuilder builder = jsonBuilderFactory.createArrayBuilder(arr);
for (int i = 0; i < arr.size(); i++) {
if (index == i) {
for (JsonValue v : c) {
builder.add(v);
}
}
builder.add(arr.get(i));
}
arr = builder.build();
return true;
}
@Override
public boolean removeAll(Collection> c) {
if (c.isEmpty()) {
return false;
}
JsonArrayBuilder builder = null;
for (int i = 0, j = 0; i < arr.size(); i++, j++) {
if (c.contains(arr.get(i))) {
if (builder == null) {
builder = jsonBuilderFactory.createArrayBuilder(arr);
}
builder.remove(j--);
}
}
if (builder != null) {
arr = builder.build();
return true;
} else {
return false;
}
}
@Override
public boolean retainAll(Collection> c) {
if (c.isEmpty()) {
arr = jsonBuilderFactory.createArrayBuilder().build();
return true;
}
JsonArrayBuilder builder = null;
for (int i = 0, j = 0; i < arr.size(); i++, j++) {
if (!c.contains(arr.get(i))) {
if (builder == null) {
builder = jsonBuilderFactory.createArrayBuilder(arr);
}
builder.remove(j--);
}
}
if (builder != null) {
arr = builder.build();
return true;
} else {
return false;
}
}
@Override
public void clear() {
arr = jsonBuilderFactory.createArrayBuilder().build();
}
@Override
public JsonValue get(int index) {
return arr.get(index);
}
@Override
public JsonValue set(int index, JsonValue element) {
if (index == arr.size()) {
arr = jsonBuilderFactory.createArrayBuilder(arr).add(index, element).build();
return null;
} else {
JsonValue oldValue = arr.get(index);
arr = jsonBuilderFactory.createArrayBuilder(arr).set(index, element).build();
return oldValue;
}
}
@Override
public void add(int index, JsonValue element) {
arr = jsonBuilderFactory.createArrayBuilder(arr).add(index, element).build();
}
@Override
public JsonValue remove(int index) {
JsonValue oldValue = arr.get(index);
arr = jsonBuilderFactory.createArrayBuilder(arr).remove(index).build();
return oldValue;
}
@Override
public int indexOf(Object o) {
return arr.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return arr.lastIndexOf(o);
}
@Override
public ListIterator listIterator() {
return listIterator(0);
}
@Override
public ListIterator listIterator(int index) {
return new ListIterator