88 Commits

Author SHA1 Message Date
Sean Leary
010e83b925 Update RELEASES.md for release 20240205 2024-02-05 20:44:18 -06:00
Sean Leary
9865dbbebe Update pom.xml for release 20240205 2024-02-05 20:23:59 -06:00
Sean Leary
4548696c8d Update README.md for release 20240205 2024-02-05 20:22:23 -06:00
Sean Leary
f0308a3475 Merge pull request #855 from stleary/Fix-stack-overflow-failures
Fixing JSONArrayTest testRecursiveDepthArrayFor1000Levels()
2024-02-05 14:18:20 -06:00
Sean Leary
19dec1bb5f Fixing JSONArrayTest testRecursiveDepthArrayFor1000Levels() 2024-02-02 13:11:37 -06:00
Sean Leary
f2d20988de Merge pull request #832 from keatontaylor10/feature-disable-whitespace-trim
Add a config flag to disable whitespace trimming
2024-01-26 19:40:36 -06:00
Keaton
7915d8518f Merge branch 'stleary:master' into feature-disable-whitespace-trim 2024-01-18 09:17:26 +02:00
Sean Leary
55b824d4c4 Merge pull request #846 from stleary/cleanup-and-merge-tests
Cleanup warnings and merge new unit tests
2024-01-04 08:52:02 -06:00
Sean Leary
ac7806d060 Merge pull request #845 from seppl831/fix-annotation-search-performance
improved annotation search performance
2024-01-01 11:43:15 -06:00
Sean Leary
86bb0a1a02 cleanup-and-merge-tests: pull in unit tests from #809 2023-12-30 17:00:02 -06:00
Sean Leary
5ddb8c3d35 cleanup-and-merge-tests: fix warnings, set gradlew permissions, enable unchecked warnings in maven 2023-12-30 16:30:19 -06:00
Thomas Gress
23ac2e7bca improved annotation search performance 2023-12-29 12:28:24 +01:00
Sean Leary
d7819a4fa2 Merge pull request #823 from sk02241994/issue_743
JSON parsing self reference object and array
2023-12-27 05:28:17 -06:00
sk02241994
7701f21839 Adding comments 2023-12-24 11:39:26 +05:30
sk02241994
ffd48afa42 Review comments 2023-12-23 10:53:54 +05:30
sk02241994
abea194120 Adding JSONParserConfiguration for configuring the depth of nested maps 2023-12-22 15:47:55 +05:30
sk02241994
dcac3bc18e Adding test case for nested json with depth of 999, 1000, 1001 2023-12-22 15:47:54 +05:30
sk02241994
6d811607dd Resolving issue #743
- Recursive depth issue found in JSONObject
- Recursive depth issue found in JSONArray
2023-12-22 15:47:54 +05:30
Keaton Taylor
4d6de8c00a Remove unused constructor and add comment above other constructor 2023-12-13 14:04:05 +02:00
Sean Leary
6dba7220e1 Merge pull request #835 from LaFriska/branch1
deleted redundant .toString() call in README test method Sysout
2023-12-07 08:20:34 -06:00
Sean Leary
d4521696a9 Merge pull request #831 from adityap27/refactor
Refactor NumberConversionUtil and toString() of CookieList & XML Classes.
2023-12-03 10:18:22 -06:00
Keaton Taylor
e430db40aa Update XMLParserConfiguration to not be static and add a comment about the use of shouldTrimWhiteSpace 2023-11-30 10:05:54 +02:00
LaFriska
7cbeb35498 deleted redundant .toString() call in README test method Sysout 2023-11-28 17:39:46 -05:00
Aditya Purohit
aba82d9cc4 isNumericChar() - switch comparison order 2023-11-28 02:56:10 +00:00
Keaton Taylor
9ee10fdfc8 Merge remote-tracking branch 'origin/master' into feature-disable-whitespace-trim 2023-11-27 11:15:49 +02:00
Aditya Purohit
4a468d163a Merge branch 'stleary:master' into refactor 2023-11-26 20:16:53 -04:00
Sean Leary
92991770ca Merge pull request #830 from HappyHacker123/upgrade_json_path
Upgrade json-path's version to 2.4.0 to avoid dependency conflict.
2023-11-25 13:51:07 -06:00
Keaton Taylor
09f35372d4 Update clone() method so that default constructor does not need to be changed 2023-11-22 11:14:50 +02:00
Keaton Taylor
30f5b2de79 Add a config flag to disable whitespace trimming 2023-11-20 17:50:22 +02:00
Aditya Purohit
7f1cb8bf62 refactor: decompose condition of digit checks by using extra method 'isNumericChar(...)' in NumberConversionUtil. 2023-11-19 09:51:44 -04:00
Aditya Purohit
75419e3f25 refactor: introduce explaining variable 'indentationSuffix' in XML.toString() 2023-11-19 09:21:05 -04:00
Aditya Purohit
097a401f3f refactor: rename variable boolean 'b' to 'isEndOfPair' in CookieList.toString() 2023-11-19 09:11:32 -04:00
Sean Leary
5c4a7a1b1f Merge pull request #828 from harshith2000/Fix-flaky-tests
Fixed flaky tests in XMLTest.java
2023-11-17 11:51:00 -06:00
HappyHacker123
b5f9febfe9 Upgrade json-path's version to 2.4 to avoid dependency conflict. 2023-11-17 21:31:06 +08:00
Saiharshith Karuneegar Ramesh
1a61af8255 Fixed flaky tests in XMLTest.java 2023-11-13 13:25:30 -06:00
Sean Leary
11c29c366d Merge pull request #824 from johnjaylward/fix821
Ignore tests that fail due to resource differences
2023-11-10 11:27:18 -06:00
John J. Aylward
a3742acf74 Fixes #821
add ignore annotation to tests that may fail due to differences in machine resources and can't be controlled via the tests
2023-11-06 17:54:09 -05:00
Sean Leary
783d298f99 Merge pull request #814 from rudrajyotib/issue813
Refactor duplicate code for stringToNumber() in JSONObject, JSONArray, and XML
2023-10-31 17:40:17 -05:00
Sean Leary
6c1bc0660a Merge pull request #820 from rudrajyotib/issue748
Close XML tag explicitly for empty tags with configuration.
2023-10-31 17:38:45 -05:00
rudrajyoti biswas
8ec822c575 #748 - PR comments - follow convention of configuration builder. 2023-10-28 07:36:31 +05:30
rudrajyoti biswas
1ceb70b525 #813 - PR comments - alignments 2023-10-28 07:09:37 +05:30
rudrajyoti biswas
c05d7058ff #748 - javadoc updated for methods. 2023-10-27 17:17:20 +05:30
rudrajyoti biswas
07a358449a Merge branch 'master' into issue813 2023-10-27 17:09:01 +05:30
rudrajyoti biswas
7fe2fd95a5 Merge branch 'master' into issue748 2023-10-27 17:07:46 +05:30
Sean Leary
b5b9f636ff Merge pull request #815 from johnjaylward/ensure_1-6_compat
Ensure java 6 compatable
2023-10-25 13:53:23 -05:00
rudrajyoti biswas
c6ec2f0e4c #748 - close XML tag explicitly for empty tags with configuration. 2023-10-25 23:23:00 +05:30
Sean Leary
1a2108efa2 Merge pull request #812 from yeikel/patch-1
docs: use syntax highlighting
2023-10-25 09:58:30 -05:00
Sean Leary
caadcba30e Merge pull request #794 from rudrajyotib/master
XML optLong/getLong equivalent updates for string to number conversion.
2023-10-25 09:55:13 -05:00
John J. Aylward
ea842b437c remove unneeded matrix build typ for java 1.6 2023-10-23 17:13:07 -04:00
John J. Aylward
a2a8240d0d upload jar files to GitHub release 2023-10-23 17:07:10 -04:00
John J. Aylward
1ab11d0802 ensure java 6 compatable 2023-10-23 15:08:21 -04:00
rudrajyoti biswas
5539722c69 #813 - address PR review comment - brought down visibility. 2023-10-23 23:03:35 +05:30
rudrajyoti biswas
98b79ae7bf #813 - moved number conversion related common changes to utility static method. 2023-10-23 19:16:25 +05:30
Rudrajyoti Biswas
04a4c5a3ec Merge branch 'stleary:master' into master 2023-10-21 12:29:31 +00:00
Yeikel
6007165c17 docs: use syntax highlighting
use syntax highlighting to improve the format of the readme
2023-10-21 00:10:42 -04:00
Sean Leary
411f71137b Merge pull request #803 from yeikel/patch-2
ci: test with Java 21
2023-10-20 07:37:32 -05:00
Sean Leary
e9117dbe5c Merge branch 'master' into patch-2 2023-10-20 07:33:03 -05:00
Sean Leary
7a85b514a9 Merge pull request #808 from theKnightsOfRohan/fix-build-messages
Fix compiler warnings
2023-10-20 07:32:07 -05:00
Sean Leary
82c8f486c5 Merge pull request #806 from johnjaylward/deploymentPipeline
Add new deployment pipeline
2023-10-20 07:31:12 -05:00
Sean Leary
006b29bda3 Merge branch 'master' into deploymentPipeline 2023-10-20 07:28:44 -05:00
Sean Leary
996d3a5fad Merge pull request #801 from johnjaylward/updateActionsForDeploy
Updates the pipeline to validate that packaging a jar works properly
2023-10-20 07:26:41 -05:00
Yeikel
de745e9c81 ci: test with Java 21 2023-10-19 22:41:47 -04:00
rudrajyoti biswas
2374766018 #790 - Update XML with changes for string to number conversion.
For now the code remains duplicated in JSON and XML parsers.
Unit test cases updated to comply with number expectations.
2023-10-19 14:07:53 +05:30
rudrajyoti biswas
1d0775cce7 Revert changes with feature and refactor together. 2023-10-19 10:28:11 +05:30
Sean Leary
d677a99f4e Merge pull request #798 from hofi1/bugfix/fix-XML-flacky-test
fix: flakiness in org.json.junit.XMLTest#testIndentComplicatedJsonObjectWithArrayAndWithConfig
2023-10-17 13:52:29 -05:00
Sean Leary
e6d37c469d Merge pull request #788 from hofi1/bugfix/fix-JSON-flakiness
Fix string compare unit tests
2023-10-17 13:51:40 -05:00
theKnightsOfRohan
f074bed732 fix(ParserConfiguration): add <T> params to docs 2023-10-16 17:48:03 -07:00
John J. Aylward
e8f125fb6e update workflow to use GPG 2023-10-16 18:23:39 -04:00
John J. Aylward
ed183e6142 remove deprecated parent pom per Sonatype docs 2023-10-16 18:22:08 -04:00
John J. Aylward
a86786a5f5 Add snapshot repository 2023-10-16 18:03:39 -04:00
John J. Aylward
7c4f98c42c Add new deployment pipeline.
This should only trigger when a release is published
2023-10-16 17:30:57 -04:00
John J. Aylward
3894483560 Add build badges to README 2023-10-16 15:44:02 -04:00
John J. Aylward
be115059e9 Correct supported java versions 2023-10-16 15:44:02 -04:00
John J. Aylward
2b41cf44b5 include jar in job artifacts 2023-10-16 15:43:55 -04:00
John J. Aylward
9a9efac2af Correct moditect configuration to work on java8 2023-10-16 15:36:33 -04:00
John J. Aylward
134074aeaa Revert "Reverting #761"
This reverts commit b180dbedbc.
2023-10-16 15:36:33 -04:00
John J. Aylward
8540bb80c0 Validate that the mvn package step completes 2023-10-16 15:36:33 -04:00
simonh5
4dfd779b1c fix: flakiness in org.json.junit.XMLTest#testIndentComplicatedJsonObjectWithArrayAndWithConfig 2023-10-14 17:21:06 -05:00
rudrajyoti biswas
7b2677ac5a #790 - Update XML with changes for string to number conversion.
Moved the code logic to a common utility to de-duplicate.
2023-10-14 10:05:36 +05:30
simonh5
29a7f4622d remove JSONAssert 2023-10-13 21:23:09 -05:00
Sean Leary
f346203cd6 Merge pull request #793 from stleary/revert-761
Reverting #761
2023-10-13 16:10:17 -05:00
Sean Leary
b180dbedbc Reverting #761 2023-10-13 16:04:14 -05:00
Sean Leary
cca6d1020f Merge pull request #792 from stleary/pre-release-20231013
update the docs for release 20231013
2023-10-13 15:40:19 -05:00
simonh5
e4aa7f1308 fix: change from JSONAssert to checking the similarity of JSONObjects 2023-10-12 21:09:27 -05:00
Simon Hofbauer
228598ca84 Merge pull request #6 from hofi1/bugfix/fix-flakyness-testToJSONObject_reversibility
fix: flakiness in JSONMLTest#testToJSONObject_reversibility
2023-10-10 18:20:20 -05:00
Simon Hofbauer
0a6fb1d578 Merge pull request #5 from hofi1/bugfix/fix-flakyness-org.json.junit.JSONObjectTest#valueToString
fix: flakiness in org.json.junit.JSONObjectTest#valueToString
2023-10-10 18:19:28 -05:00
simonh5
ca88454f1c fix: flakiness in org.json.junit.JSONObjectTest#valueToString 2023-09-19 14:28:06 -05:00
simonh5
becc1631e6 fix: flakiness in JSONMLTest#testToJSONObject_reversibility 2023-09-18 20:20:13 -05:00
25 changed files with 1190 additions and 283 deletions

82
.github/workflows/deployment.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
# For more information see:
# * https://docs.github.com/en/actions/learn-github-actions
# * https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
# * https://github.com/actions/setup-java/blob/v3.13.0/docs/advanced-usage.md#Publishing-using-Apache-Maven
#
name: Deployment workflow
on:
release:
types: [published]
jobs:
# old-school build and jar method. No tests run or compiled.
publish-1_6:
name: Publish Java 1.6 to GitHub Release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Setup java
uses: actions/setup-java@v1
with:
java-version: 1.6
- name: Compile Java 1.6
run: |
mkdir -p target/classes
javac -version
javac -source 1.6 -target 1.6 -d target/classes/ src/main/java/org/json/*.java
- name: Create JAR 1.6
run: |
jar cvf "target/org.json-1.6-${{ github.ref_name }}.jar" -C target/classes .
- name: Add 1.6 Jar To Release
uses: softprops/action-gh-release@v1
with:
append_body: true
files: |
target/*.jar
publish:
name: Publish Java 8 to Maven Central and GitHub Release
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Java for publishing to Maven Central Repository
uses: actions/setup-java@v3
with:
# Use lowest supported LTS Java version
java-version: '8'
distribution: 'temurin'
server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
server-username: MAVEN_USERNAME # env variable for username in deploy
server-password: MAVEN_PASSWORD # env variable for token in deploy
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
- name: Publish to the Maven Central Repository
run: mvn --batch-mode deploy
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
- name: Add Jar To Release
uses: softprops/action-gh-release@v1
with:
append_body: true
files: |
target/*.jar
# - name: Set up Java for publishing to GitHub Packages
# uses: actions/setup-java@v3
# with:
# # Use lowest supported LTS Java version
# java-version: '8'
# distribution: 'temurin'
# - name: Publish to GitHub Packages
# run: mvn --batch-mode deploy
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,5 +1,5 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
# For more information see: https://docs.github.com/en/actions/learn-github-actions or https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
name: Java CI with Maven
@@ -10,12 +10,38 @@ on:
branches: [ master ]
jobs:
# old-school build and jar method. No tests run or compiled.
build-1_6:
name: Java 1.6
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup java
uses: actions/setup-java@v1
with:
java-version: 1.6
- name: Compile Java 1.6
run: |
mkdir -p target/classes
javac -version
javac -source 1.6 -target 1.6 -d target/classes/ src/main/java/org/json/*.java
- name: Create java 1.6 JAR
run: |
jar cvf target/org.json.jar -C target/classes .
- name: Upload JAR 1.6
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: Create java 1.6 JAR
path: target/*.jar
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 2
matrix:
# build against supported Java LTS versions:
java: [ 8, 11, 17 ]
java: [ 8, 11, 17, 21 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v3
@@ -26,24 +52,32 @@ jobs:
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Compile Java ${{ matrix.java }}
run: mvn clean compile -Dmaven.compiler.source=${{ matrix.java }} -Dmaven.compiler.target=${{ matrix.java }} -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true
run: mvn clean compile -D maven.compiler.source=${{ matrix.java }} -D maven.compiler.target=${{ matrix.java }} -D maven.test.skip=true -D maven.site.skip=true -D maven.javadoc.skip=true
- name: Run Tests ${{ matrix.java }}
run: |
mvn test -Dmaven.compiler.source=${{ matrix.java }} -Dmaven.compiler.target=${{ matrix.java }}
mvn test -D maven.compiler.source=${{ matrix.java }} -D maven.compiler.target=${{ matrix.java }}
- name: Build Test Report ${{ matrix.java }}
if: ${{ always() }}
run: |
mvn surefire-report:report-only -Dmaven.compiler.source=${{ matrix.java }} -Dmaven.compiler.target=${{ matrix.java }}
mvn site -DgenerateReports=false -Dmaven.compiler.source=${{ matrix.java }} -Dmaven.compiler.target=${{ matrix.java }}
mvn surefire-report:report-only -D maven.compiler.source=${{ matrix.java }} -D maven.compiler.target=${{ matrix.java }}
mvn site -D generateReports=false -D maven.compiler.source=${{ matrix.java }} -D maven.compiler.target=${{ matrix.java }}
- name: Upload Test Results ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: Test Results ${{ matrix.java }}
path: target/surefire-reports/
- name: Upload Test Report ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v3
with:
name: Test Report ${{ matrix.java }}
path: target/site/
- name: Package Jar ${{ matrix.java }}
run: mvn clean package -D maven.compiler.source=${{ matrix.java }} -D maven.compiler.target=${{ matrix.java }} -D maven.test.skip=true -D maven.site.skip=true
- name: Upload Package Results ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: Package Jar ${{ matrix.java }}
path: target/*.jar

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
# ignore eclipse project files
.project
.classpath
# ignore vscode files
.vscode
# ignore Intellij Idea project files
.idea
*.iml

View File

@@ -7,8 +7,10 @@ JSON in Java [package org.json]
===============================
[![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json)
[![Java CI with Maven](https://github.com/stleary/JSON-java/actions/workflows/pipeline.yml/badge.svg)](https://github.com/stleary/JSON-java/actions/workflows/pipeline.yml)
[![CodeQL](https://github.com/stleary/JSON-java/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/stleary/JSON-java/actions/workflows/codeql-analysis.yml)
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20231013/json-20231013.jar)**
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20240205/json-20240205.jar)**
# Overview
@@ -24,7 +26,8 @@ Project goals include:
* No external dependencies
* Fast execution and low memory footprint
* Maintain backward compatibility
* Designed and tested to use on Java versions 1.6 - 1.11
* Designed and tested to use on Java versions 1.6 - 21
The files in this package implement JSON encoders and decoders. The package can also convert between JSON and XML, HTTP headers, Cookies, and CDL.
@@ -41,56 +44,56 @@ The org.json package can be built from the command line, Maven, and Gradle. The
**Building from the command line**
*Build the class files from the package root directory src/main/java*
````
```shell
javac org/json/*.java
````
```
*Create the jar file in the current directory*
````
```shell
jar cf json-java.jar org/json/*.class
````
```
*Compile a program that uses the jar (see example code below)*
````
```shell
javac -cp .;json-java.jar Test.java (Windows)
javac -cp .:json-java.jar Test.java (Unix Systems)
````
```
*Test file contents*
````
```java
import org.json.JSONObject;
public class Test {
public static void main(String args[]){
JSONObject jo = new JSONObject("{ \"abc\" : \"def\" }");
System.out.println(jo.toString());
System.out.println(jo);
}
}
````
```
*Execute the Test file*
````
```shell
java -cp .;json-java.jar Test (Windows)
java -cp .:json-java.jar Test (Unix Systems)
````
```
*Expected output*
````
```json
{"abc":"def"}
````
```
**Tools to build the package and execute the unit tests**
Execute the test suite with Maven:
```
```shell
mvn clean test
```
Execute the test suite with Gradlew:
```
```shell
gradlew clean build test
```

View File

@@ -21,7 +21,7 @@ repositories {
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.jayway.jsonpath:json-path:2.1.0'
testImplementation 'com.jayway.jsonpath:json-path:2.4.0'
testImplementation 'org.mockito:mockito-core:4.2.0'
}

View File

@@ -5,6 +5,8 @@ and artifactId "json". For example:
[https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav)
~~~
20240205 Recent commits.
20231013 First release with minimum Java version 1.8. Recent commits, including fixes for CVE-2023-5072.
20230618 Final release with Java 1.6 compatibility. Future releases will require Java 1.8 or greater.

0
gradlew vendored Normal file → Executable file
View File

37
pom.xml
View File

@@ -3,7 +3,7 @@
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
<version>20240205</version>
<packaging>bundle</packaging>
<name>JSON in Java</name>
@@ -21,12 +21,6 @@
</description>
<url>https://github.com/douglascrockford/JSON-java</url>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>9</version>
</parent>
<scm>
<url>https://github.com/douglascrockford/JSON-java.git</url>
<connection>scm:git:git://github.com/douglascrockford/JSON-java.git</connection>
@@ -53,6 +47,19 @@
</properties>
<distributionManagement>
<repository>
<id>ossrh</id>
<name>Central Repository OSSRH</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -63,7 +70,7 @@
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.1.0</version>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -97,6 +104,9 @@
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-Xlint:unchecked</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
@@ -173,12 +183,11 @@
<configuration>
<jvmVersion>9</jvmVersion>
<module>
<moduleInfo>
<name>org.json</name>
<exports>
org.json;
</exports>
</moduleInfo>
<moduleInfoSource>
module org.json {
exports org.json;
}
</moduleInfoSource>
</module>
</configuration>
</execution>

View File

@@ -46,19 +46,19 @@ public class CookieList {
* @throws JSONException if a called function fails
*/
public static String toString(JSONObject jo) throws JSONException {
boolean b = false;
boolean isEndOfPair = false;
final StringBuilder sb = new StringBuilder();
// Don't use the new entrySet API to maintain Android support
for (final String key : jo.keySet()) {
final Object value = jo.opt(key);
if (!JSONObject.NULL.equals(value)) {
if (b) {
if (isEndOfPair) {
sb.append(';');
}
sb.append(Cookie.escape(key));
sb.append("=");
sb.append(Cookie.escape(value.toString()));
b = true;
isEndOfPair = true;
}
}
return sb.toString();

View File

@@ -149,11 +149,40 @@ public class JSONArray implements Iterable<Object> {
* A Collection.
*/
public JSONArray(Collection<?> collection) {
this(collection, 0, new JSONParserConfiguration());
}
/**
* Construct a JSONArray from a Collection.
*
* @param collection
* A Collection.
* @param jsonParserConfiguration
* Configuration object for the JSON parser
*/
public JSONArray(Collection<?> collection, JSONParserConfiguration jsonParserConfiguration) {
this(collection, 0, jsonParserConfiguration);
}
/**
* Construct a JSONArray from a collection with recursion depth.
*
* @param collection
* A Collection.
* @param recursionDepth
* Variable for tracking the count of nested object creations.
* @param jsonParserConfiguration
* Configuration object for the JSON parser
*/
JSONArray(Collection<?> collection, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONArray has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
}
if (collection == null) {
this.myArrayList = new ArrayList<Object>();
} else {
this.myArrayList = new ArrayList<Object>(collection.size());
this.addAll(collection, true);
this.addAll(collection, true, recursionDepth, jsonParserConfiguration);
}
}
@@ -205,7 +234,7 @@ public class JSONArray implements Iterable<Object> {
throw new JSONException(
"JSONArray initial value should be a string or collection or array.");
}
this.addAll(array, true);
this.addAll(array, true, 0);
}
/**
@@ -331,7 +360,7 @@ public class JSONArray implements Iterable<Object> {
if (object instanceof Number) {
return (Number)object;
}
return JSONObject.stringToNumber(object.toString());
return NumberConversionUtil.stringToNumber(object.toString());
} catch (Exception e) {
throw wrongValueFormatException(index, "number", object, e);
}
@@ -1078,7 +1107,7 @@ public class JSONArray implements Iterable<Object> {
if (val instanceof String) {
try {
return JSONObject.stringToNumber((String) val);
return NumberConversionUtil.stringToNumber((String) val);
} catch (Exception e) {
return defaultValue;
}
@@ -1330,7 +1359,8 @@ public class JSONArray implements Iterable<Object> {
* The subscript.
* @param value
* The Map value.
* @return this.
* @return
* reference to self
* @throws JSONException
* If the index is negative or if the value is an invalid
* number.
@@ -1338,7 +1368,27 @@ public class JSONArray implements Iterable<Object> {
* If a key in the map is <code>null</code>
*/
public JSONArray put(int index, Map<?, ?> value) throws JSONException {
this.put(index, new JSONObject(value));
this.put(index, new JSONObject(value, new JSONParserConfiguration()));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject that
* is produced from a Map.
*
* @param index
* The subscript
* @param value
* The Map value.
* @param jsonParserConfiguration
* Configuration object for the JSON parser
* @return reference to self
* @throws JSONException
* If the index is negative or if the value is an invalid
* number.
*/
public JSONArray put(int index, Map<?, ?> value, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this.put(index, new JSONObject(value, jsonParserConfiguration));
return this;
}
@@ -1779,13 +1829,14 @@ public class JSONArray implements Iterable<Object> {
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
*
* @param recursionDepth
* Variable for tracking the count of nested object creations.
*/
private void addAll(Collection<?> collection, boolean wrap) {
private void addAll(Collection<?> collection, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size());
if (wrap) {
for (Object o: collection){
this.put(JSONObject.wrap(o));
this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration));
}
} else {
for (Object o: collection){
@@ -1814,7 +1865,24 @@ public class JSONArray implements Iterable<Object> {
}
}
}
/**
* Add an array's elements to the JSONArray.
*
* @param array
* Array. If the parameter passed is null, or not an array,
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
* @throws JSONException
* If not an array or if an array value is non-finite number.
*/
private void addAll(Object array, boolean wrap) throws JSONException {
this.addAll(array, wrap, 0);
}
/**
* Add an array's elements to the JSONArray.
*
@@ -1823,21 +1891,40 @@ public class JSONArray implements Iterable<Object> {
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
* @param recursionDepth
* Variable for tracking the count of nested object creations.
*/
private void addAll(Object array, boolean wrap, int recursionDepth) {
addAll(array, wrap, recursionDepth, new JSONParserConfiguration());
}
/**
* Add an array's elements to the JSONArray.
*`
* @param array
* Array. If the parameter passed is null, or not an array,
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
*
* @param recursionDepth
* Variable for tracking the count of nested object creations.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
* @throws JSONException
* If not an array or if an array value is non-finite number.
* @throws NullPointerException
* Thrown if the array parameter is null.
*/
private void addAll(Object array, boolean wrap) throws JSONException {
private void addAll(Object array, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
if (array.getClass().isArray()) {
int length = Array.getLength(array);
this.myArrayList.ensureCapacity(this.myArrayList.size() + length);
if (wrap) {
for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i)));
this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1, jsonParserConfiguration));
}
} else {
for (int i = 0; i < length; i += 1) {
@@ -1850,7 +1937,7 @@ public class JSONArray implements Iterable<Object> {
// JSONArray
this.myArrayList.addAll(((JSONArray)array).myArrayList);
} else if (array instanceof Collection) {
this.addAll((Collection<?>)array, wrap);
this.addAll((Collection<?>)array, wrap, recursionDepth);
} else if (array instanceof Iterable) {
this.addAll((Iterable<?>)array, wrap);
} else {

View File

@@ -55,11 +55,13 @@ public class JSONMLParserConfiguration extends ParserConfiguration {
);
}
@SuppressWarnings("unchecked")
@Override
public JSONMLParserConfiguration withKeepStrings(final boolean newVal) {
return super.withKeepStrings(newVal);
}
@SuppressWarnings("unchecked")
@Override
public JSONMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);

View File

@@ -28,6 +28,9 @@ import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Pattern;
import static org.json.NumberConversionUtil.potentialNumber;
import static org.json.NumberConversionUtil.stringToNumber;
/**
* A JSONObject is an unordered collection of name/value pairs. Its external
* form is a string wrapped in curly braces with colons between the names and
@@ -273,6 +276,30 @@ public class JSONObject {
* If a key in the map is <code>null</code>
*/
public JSONObject(Map<?, ?> m) {
this(m, 0, new JSONParserConfiguration());
}
/**
* Construct a JSONObject from a Map with custom json parse configurations.
*
* @param m
* A map object that can be used to initialize the contents of
* the JSONObject.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
*/
public JSONObject(Map<?, ?> m, JSONParserConfiguration jsonParserConfiguration) {
this(m, 0, jsonParserConfiguration);
}
/**
* Construct a JSONObject from a map with recursion depth.
*
*/
private JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
}
if (m == null) {
this.map = new HashMap<String, Object>();
} else {
@@ -284,7 +311,7 @@ public class JSONObject {
final Object value = e.getValue();
if (value != null) {
testValidity(value);
this.map.put(String.valueOf(e.getKey()), wrap(value));
this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration));
}
}
}
@@ -1838,6 +1865,10 @@ public class JSONObject {
}
}
//If the superclass is Object, no annotations will be found any more
if (c.getSuperclass().equals(Object.class))
return null;
try {
return getAnnotation(
c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()),
@@ -1892,6 +1923,10 @@ public class JSONObject {
}
}
//If the superclass is Object, no annotations will be found any more
if (c.getSuperclass().equals(Object.class))
return -1;
try {
int d = getAnnotationDepth(
c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()),
@@ -2380,84 +2415,6 @@ public class JSONObject {
|| val.indexOf('E') > -1 || "-0".equals(val);
}
/**
* Converts a string to a number using the narrowest possible type. Possible
* returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
* When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
*
* @param input value to convert
* @return Number representation of the value.
* @throws NumberFormatException thrown if the value is not a valid number. A public
* caller should catch this and wrap it in a {@link JSONException} if applicable.
*/
protected static Number stringToNumber(final String input) throws NumberFormatException {
String val = input;
if (val.startsWith(".")){
val = "0"+val;
}
if (val.startsWith("-.")){
val = "-0."+val.substring(2);
}
char initial = val.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-' ) {
// decimal representation
if (isDecimalNotation(val)) {
// Use a BigDecimal all the time so we keep the original
// representation. BigDecimal doesn't support -0.0, ensure we
// keep that by forcing a decimal.
try {
BigDecimal bd = new BigDecimal(val);
if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
return Double.valueOf(-0.0);
}
return bd;
} catch (NumberFormatException retryAsDouble) {
// this is to support "Hex Floats" like this: 0x1.0P-1074
try {
Double d = Double.valueOf(val);
if(d.isNaN() || d.isInfinite()) {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
return d;
} catch (NumberFormatException ignore) {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
}
}
val = removeLeadingZerosOfNumber(input);
initial = val.charAt(0);
if(initial == '0' && val.length() > 1) {
char at1 = val.charAt(1);
if(at1 >= '0' && at1 <= '9') {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
} else if (initial == '-' && val.length() > 2) {
char at1 = val.charAt(1);
char at2 = val.charAt(2);
if(at1 == '0' && at2 >= '0' && at2 <= '9') {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
}
// integer representation.
// This will narrow any values to the smallest reasonable Object representation
// (Integer, Long, or BigInteger)
// BigInteger down conversion: We use a similar bitLength compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is
// long lived.
BigInteger bi = new BigInteger(val);
if(bi.bitLength() <= 31){
return Integer.valueOf(bi.intValue());
}
if(bi.bitLength() <= 63){
return Long.valueOf(bi.longValue());
}
return bi;
}
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
/**
* Try to convert a string into a number, boolean, or null. If the string
* can't be converted, return the string.
@@ -2501,26 +2458,7 @@ public class JSONObject {
}
private static boolean potentialNumber(String value){
if (value == null || value.isEmpty()){
return false;
}
return potentialPositiveNumberStartingAtIndex(value, (value.charAt(0)=='-'?1:0));
}
private static boolean potentialPositiveNumberStartingAtIndex(String value,int index){
if (index >= value.length()){
return false;
}
return digitAtIndex(value, (value.charAt(index)=='.'?index+1:index));
}
private static boolean digitAtIndex(String value, int index){
if (index >= value.length()){
return false;
}
return value.charAt(index) >= '0' && value.charAt(index) <= '9';
}
/**
* Throw an exception if the object is a NaN or infinite number.
@@ -2660,7 +2598,31 @@ public class JSONObject {
return wrap(object, null);
}
/**
* Wrap an object, if necessary. If the object is <code>null</code>, return the NULL
* object. If it is an array or collection, wrap it in a JSONArray. If it is
* a map, wrap it in a JSONObject. If it is a standard property (Double,
* String, et al) then it is already wrapped. Otherwise, if it comes from
* one of the java packages, turn it into a string. And if it doesn't, try
* to wrap it in a JSONObject. If the wrapping fails, then null is returned.
*
* @param object
* The object to wrap
* @param recursionDepth
* Variable for tracking the count of nested object creations.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
* @return The wrapped value
*/
static Object wrap(Object object, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
return wrap(object, null, recursionDepth, jsonParserConfiguration);
}
private static Object wrap(Object object, Set<Object> objectsRecord) {
return wrap(object, objectsRecord, 0, new JSONParserConfiguration());
}
private static Object wrap(Object object, Set<Object> objectsRecord, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
try {
if (NULL.equals(object)) {
return NULL;
@@ -2678,14 +2640,14 @@ public class JSONObject {
if (object instanceof Collection) {
Collection<?> coll = (Collection<?>) object;
return new JSONArray(coll);
return new JSONArray(coll, recursionDepth, jsonParserConfiguration);
}
if (object.getClass().isArray()) {
return new JSONArray(object);
}
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
return new JSONObject(map);
return new JSONObject(map, recursionDepth, jsonParserConfiguration);
}
Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null ? objectPackage
@@ -2922,23 +2884,5 @@ public class JSONObject {
);
}
/**
* For a prospective number, remove the leading zeros
* @param value prospective number
* @return number without leading zeros
*/
private static String removeLeadingZerosOfNumber(String value){
if (value.equals("-")){return value;}
boolean negativeFirstChar = (value.charAt(0) == '-');
int counter = negativeFirstChar ? 1:0;
while (counter < value.length()){
if (value.charAt(counter) != '0'){
if (negativeFirstChar) {return "-".concat(value.substring(counter));}
return value.substring(counter);
}
++counter;
}
if (negativeFirstChar) {return "-0";}
return "0";
}
}

View File

@@ -0,0 +1,26 @@
package org.json;
/**
* Configuration object for the JSON parser. The configuration is immutable.
*/
public class JSONParserConfiguration extends ParserConfiguration {
/**
* Configuration with the default values.
*/
public JSONParserConfiguration() {
super();
}
@Override
protected JSONParserConfiguration clone() {
return new JSONParserConfiguration();
}
@SuppressWarnings("unchecked")
@Override
public JSONParserConfiguration withMaxNestingDepth(final int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);
}
}

View File

@@ -0,0 +1,152 @@
package org.json;
import java.math.BigDecimal;
import java.math.BigInteger;
class NumberConversionUtil {
/**
* Converts a string to a number using the narrowest possible type. Possible
* returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
* When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
*
* @param input value to convert
* @return Number representation of the value.
* @throws NumberFormatException thrown if the value is not a valid number. A public
* caller should catch this and wrap it in a {@link JSONException} if applicable.
*/
static Number stringToNumber(final String input) throws NumberFormatException {
String val = input;
if (val.startsWith(".")){
val = "0"+val;
}
if (val.startsWith("-.")){
val = "-0."+val.substring(2);
}
char initial = val.charAt(0);
if ( isNumericChar(initial) || initial == '-' ) {
// decimal representation
if (isDecimalNotation(val)) {
// Use a BigDecimal all the time so we keep the original
// representation. BigDecimal doesn't support -0.0, ensure we
// keep that by forcing a decimal.
try {
BigDecimal bd = new BigDecimal(val);
if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
return Double.valueOf(-0.0);
}
return bd;
} catch (NumberFormatException retryAsDouble) {
// this is to support "Hex Floats" like this: 0x1.0P-1074
try {
Double d = Double.valueOf(val);
if(d.isNaN() || d.isInfinite()) {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
return d;
} catch (NumberFormatException ignore) {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
}
}
val = removeLeadingZerosOfNumber(input);
initial = val.charAt(0);
if(initial == '0' && val.length() > 1) {
char at1 = val.charAt(1);
if(isNumericChar(at1)) {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
} else if (initial == '-' && val.length() > 2) {
char at1 = val.charAt(1);
char at2 = val.charAt(2);
if(at1 == '0' && isNumericChar(at2)) {
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
}
// integer representation.
// This will narrow any values to the smallest reasonable Object representation
// (Integer, Long, or BigInteger)
// BigInteger down conversion: We use a similar bitLength compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is
// long lived.
BigInteger bi = new BigInteger(val);
if(bi.bitLength() <= 31){
return Integer.valueOf(bi.intValue());
}
if(bi.bitLength() <= 63){
return Long.valueOf(bi.longValue());
}
return bi;
}
throw new NumberFormatException("val ["+input+"] is not a valid number.");
}
/**
* Checks if the character is a numeric digit ('0' to '9').
*
* @param c The character to be checked.
* @return true if the character is a numeric digit, false otherwise.
*/
private static boolean isNumericChar(char c) {
return (c <= '9' && c >= '0');
}
/**
* Checks if the value could be considered a number in decimal number system.
* @param value
* @return
*/
static boolean potentialNumber(String value){
if (value == null || value.isEmpty()){
return false;
}
return potentialPositiveNumberStartingAtIndex(value, (value.charAt(0)=='-'?1:0));
}
/**
* Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
*
* @param val value to test
* @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise.
*/
private static boolean isDecimalNotation(final String val) {
return val.indexOf('.') > -1 || val.indexOf('e') > -1
|| val.indexOf('E') > -1 || "-0".equals(val);
}
private static boolean potentialPositiveNumberStartingAtIndex(String value,int index){
if (index >= value.length()){
return false;
}
return digitAtIndex(value, (value.charAt(index)=='.'?index+1:index));
}
private static boolean digitAtIndex(String value, int index){
if (index >= value.length()){
return false;
}
return value.charAt(index) >= '0' && value.charAt(index) <= '9';
}
/**
* For a prospective number, remove the leading zeros
* @param value prospective number
* @return number without leading zeros
*/
private static String removeLeadingZerosOfNumber(String value){
if (value.equals("-")){return value;}
boolean negativeFirstChar = (value.charAt(0) == '-');
int counter = negativeFirstChar ? 1:0;
while (counter < value.length()){
if (value.charAt(counter) != '0'){
if (negativeFirstChar) {return "-".concat(value.substring(counter));}
return value.substring(counter);
}
++counter;
}
if (negativeFirstChar) {return "-0";}
return "0";
}
}

View File

@@ -71,9 +71,11 @@ public class ParserConfiguration {
*
* @param newVal
* new value to use for the <code>keepStrings</code> configuration option.
*
* @param <T> the type of the configuration object
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
public <T extends ParserConfiguration> T withKeepStrings(final boolean newVal) {
T newConfig = (T)this.clone();
newConfig.keepStrings = newVal;
@@ -96,8 +98,11 @@ public class ParserConfiguration {
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
* which means the parses will go as deep as the maximum call stack size allows.
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @param <T> the type of the configuration object
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
public <T extends ParserConfiguration> T withMaxNestingDepth(int maxNestingDepth) {
T newConfig = (T)this.clone();

View File

@@ -10,6 +10,9 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import static org.json.NumberConversionUtil.potentialNumber;
import static org.json.NumberConversionUtil.stringToNumber;
/**
* This provides static methods to convert an XML text into a JSONObject, and to
@@ -428,6 +431,9 @@ public class XML {
&& jsonObject.opt(config.getcDataTagName()) != null) {
context.accumulate(tagName, jsonObject.opt(config.getcDataTagName()));
} else {
if (!config.shouldTrimWhiteSpace()) {
removeEmpty(jsonObject, config);
}
context.accumulate(tagName, jsonObject);
}
}
@@ -442,6 +448,48 @@ public class XML {
}
}
}
/**
* This method removes any JSON entry which has the key set by XMLParserConfiguration.cDataTagName
* and contains whitespace as this is caused by whitespace between tags. See test XMLTest.testNestedWithWhitespaceTrimmingDisabled.
* @param jsonObject JSONObject which may require deletion
* @param config The XMLParserConfiguration which includes the cDataTagName
*/
private static void removeEmpty(final JSONObject jsonObject, final XMLParserConfiguration config) {
if (jsonObject.has(config.getcDataTagName())) {
final Object s = jsonObject.get(config.getcDataTagName());
if (s instanceof String) {
if (isStringAllWhiteSpace(s.toString())) {
jsonObject.remove(config.getcDataTagName());
}
}
else if (s instanceof JSONArray) {
final JSONArray sArray = (JSONArray) s;
for (int k = sArray.length()-1; k >= 0; k--){
final Object eachString = sArray.get(k);
if (eachString instanceof String) {
String s1 = (String) eachString;
if (isStringAllWhiteSpace(s1)) {
sArray.remove(k);
}
}
}
if (sArray.isEmpty()) {
jsonObject.remove(config.getcDataTagName());
}
}
}
}
private static boolean isStringAllWhiteSpace(final String s) {
for (int k = 0; k<s.length(); k++){
final char eachChar = s.charAt(k);
if (!Character.isWhitespace(eachChar)) {
return false;
}
}
return true;
}
/**
* This method tries to convert the given string value to the target object
@@ -486,8 +534,7 @@ public class XML {
* produced, then the value will just be a string.
*/
char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
if (potentialNumber(string)) {
try {
return stringToNumber(string);
} catch (Exception ignore) {
@@ -496,76 +543,9 @@ public class XML {
return string;
}
/**
* direct copy of {@link JSONObject#stringToNumber(String)} to maintain Android support.
*/
private static Number stringToNumber(final String val) throws NumberFormatException {
char initial = val.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
// decimal representation
if (isDecimalNotation(val)) {
// Use a BigDecimal all the time so we keep the original
// representation. BigDecimal doesn't support -0.0, ensure we
// keep that by forcing a decimal.
try {
BigDecimal bd = new BigDecimal(val);
if(initial == '-' && BigDecimal.ZERO.compareTo(bd)==0) {
return Double.valueOf(-0.0);
}
return bd;
} catch (NumberFormatException retryAsDouble) {
// this is to support "Hex Floats" like this: 0x1.0P-1074
try {
Double d = Double.valueOf(val);
if(d.isNaN() || d.isInfinite()) {
throw new NumberFormatException("val ["+val+"] is not a valid number.");
}
return d;
} catch (NumberFormatException ignore) {
throw new NumberFormatException("val ["+val+"] is not a valid number.");
}
}
}
// block items like 00 01 etc. Java number parsers treat these as Octal.
if(initial == '0' && val.length() > 1) {
char at1 = val.charAt(1);
if(at1 >= '0' && at1 <= '9') {
throw new NumberFormatException("val ["+val+"] is not a valid number.");
}
} else if (initial == '-' && val.length() > 2) {
char at1 = val.charAt(1);
char at2 = val.charAt(2);
if(at1 == '0' && at2 >= '0' && at2 <= '9') {
throw new NumberFormatException("val ["+val+"] is not a valid number.");
}
}
// integer representation.
// This will narrow any values to the smallest reasonable Object representation
// (Integer, Long, or BigInteger)
// BigInteger down conversion: We use a similar bitLength compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is
// long lived.
BigInteger bi = new BigInteger(val);
if(bi.bitLength() <= 31){
return Integer.valueOf(bi.intValue());
}
if(bi.bitLength() <= 63){
return Long.valueOf(bi.longValue());
}
return bi;
}
throw new NumberFormatException("val ["+val+"] is not a valid number.");
}
/**
* direct copy of {@link JSONObject#isDecimalNotation(String)} to maintain Android support.
*/
private static boolean isDecimalNotation(final String val) {
return val.indexOf('.') > -1 || val.indexOf('e') > -1
|| val.indexOf('E') > -1 || "-0".equals(val);
}
/**
@@ -659,7 +639,7 @@ public class XML {
*/
public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration config) throws JSONException {
JSONObject jo = new JSONObject();
XMLTokener x = new XMLTokener(reader);
XMLTokener x = new XMLTokener(reader, config);
while (x.more()) {
x.skipPast("<");
if(x.more()) {
@@ -850,12 +830,25 @@ public class XML {
}
}
} else if ("".equals(value)) {
sb.append(indent(indent));
sb.append('<');
sb.append(key);
sb.append("/>");
if(indentFactor > 0){
sb.append("\n");
if (config.isCloseEmptyTag()){
sb.append(indent(indent));
sb.append('<');
sb.append(key);
sb.append(">");
sb.append("</");
sb.append(key);
sb.append(">");
if (indentFactor > 0) {
sb.append("\n");
}
}else {
sb.append(indent(indent));
sb.append('<');
sb.append(key);
sb.append("/>");
if (indentFactor > 0) {
sb.append("\n");
}
}
// Emit a new tag <k>
@@ -899,14 +892,14 @@ public class XML {
string = (object == null) ? "null" : escape(object.toString());
String indentationSuffix = (indentFactor > 0) ? "\n" : "";
if(tagName == null){
return indent(indent) + "\"" + string + "\"" + ((indentFactor > 0) ? "\n" : "");
return indent(indent) + "\"" + string + "\"" + indentationSuffix;
} else if(string.length() == 0){
return indent(indent) + "<" + tagName + "/>" + ((indentFactor > 0) ? "\n" : "");
return indent(indent) + "<" + tagName + "/>" + indentationSuffix;
} else {
return indent(indent) + "<" + tagName
+ ">" + string + "</" + tagName + ">" + ((indentFactor > 0) ? "\n" : "");
+ ">" + string + "</" + tagName + ">" + indentationSuffix;
}
}
@@ -973,4 +966,5 @@ public class XML {
}
return sb.toString();
}
}

View File

@@ -43,6 +43,13 @@ public class XMLParserConfiguration extends ParserConfiguration {
*/
private boolean convertNilAttributeToNull;
/**
* When creating an XML from JSON Object, an empty tag by default will self-close.
* If it has to be closed explicitly, with empty content between start and end tag,
* this flag is to be turned on.
*/
private boolean closeEmptyTag;
/**
* This will allow type conversion for values in XML if xsi:type attribute is defined
*/
@@ -54,9 +61,18 @@ public class XMLParserConfiguration extends ParserConfiguration {
*/
private Set<String> forceList;
/**
* Flag to indicate whether white space should be trimmed when parsing XML.
* The default behaviour is to trim white space. When this is set to false, inputting XML
* with tags that are the same as the value of cDataTagName is unsupported. It is recommended to set cDataTagName
* to a distinct value in this case.
*/
private boolean shouldTrimWhiteSpace;
/**
* Default parser configuration. Does not keep strings (tries to implicitly convert
* values), and the CDATA Tag Name is "content".
* values), and the CDATA Tag Name is "content". Trims whitespace.
*/
public XMLParserConfiguration () {
super();
@@ -64,6 +80,7 @@ public class XMLParserConfiguration extends ParserConfiguration {
this.convertNilAttributeToNull = false;
this.xsiTypeMap = Collections.emptyMap();
this.forceList = Collections.emptySet();
this.shouldTrimWhiteSpace = true;
}
/**
@@ -142,15 +159,17 @@ public class XMLParserConfiguration extends ParserConfiguration {
* xsi:type="integer" as integer, xsi:type="string" as string
* @param forceList <code>new HashSet<String>()</code> to parse the provided tags' values as arrays
* @param maxNestingDepth <code>int</code> to limit the nesting depth
* @param closeEmptyTag <code>boolean</code> to turn on explicit end tag for tag with empty value
*/
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList,
final int maxNestingDepth) {
final int maxNestingDepth, final boolean closeEmptyTag) {
super(keepStrings, maxNestingDepth);
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull;
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
this.forceList = Collections.unmodifiableSet(forceList);
this.closeEmptyTag = closeEmptyTag;
}
/**
@@ -163,14 +182,17 @@ public class XMLParserConfiguration extends ParserConfiguration {
// item, a new map instance should be created and if possible each value in the
// map should be cloned as well. If the values of the map are known to also
// be immutable, then a shallow clone of the map is acceptable.
return new XMLParserConfiguration(
final XMLParserConfiguration config = new XMLParserConfiguration(
this.keepStrings,
this.cDataTagName,
this.convertNilAttributeToNull,
this.xsiTypeMap,
this.forceList,
this.maxNestingDepth
this.maxNestingDepth,
this.closeEmptyTag
);
config.shouldTrimWhiteSpace = this.shouldTrimWhiteSpace;
return config;
}
/**
@@ -182,6 +204,7 @@ public class XMLParserConfiguration extends ParserConfiguration {
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
@Override
public XMLParserConfiguration withKeepStrings(final boolean newVal) {
return super.withKeepStrings(newVal);
@@ -299,8 +322,40 @@ public class XMLParserConfiguration extends ParserConfiguration {
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
@Override
public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);
}
/**
* To enable explicit end tag with empty value.
* @param closeEmptyTag new value for the closeEmptyTag property
* @return same instance of configuration with empty tag config updated
*/
public XMLParserConfiguration withCloseEmptyTag(boolean closeEmptyTag){
XMLParserConfiguration clonedConfiguration = this.clone();
clonedConfiguration.closeEmptyTag = closeEmptyTag;
return clonedConfiguration;
}
/**
* Sets whether whitespace should be trimmed inside of tags. *NOTE* Do not use this if
* you expect your XML tags to have names that are the same as cDataTagName as this is unsupported.
* cDataTagName should be set to a distinct value in these cases.
* @param shouldTrimWhiteSpace boolean to set trimming on or off. Off is default.
* @return same instance of configuration with empty tag config updated
*/
public XMLParserConfiguration withShouldTrimWhitespace(boolean shouldTrimWhiteSpace){
XMLParserConfiguration clonedConfiguration = this.clone();
clonedConfiguration.shouldTrimWhiteSpace = shouldTrimWhiteSpace;
return clonedConfiguration;
}
public boolean isCloseEmptyTag() {
return this.closeEmptyTag;
}
public boolean shouldTrimWhiteSpace() {
return this.shouldTrimWhiteSpace;
}
}

View File

@@ -20,6 +20,8 @@ public class XMLTokener extends JSONTokener {
*/
public static final java.util.HashMap<String, Character> entity;
private XMLParserConfiguration configuration = XMLParserConfiguration.ORIGINAL;
static {
entity = new java.util.HashMap<String, Character>(8);
entity.put("amp", XML.AMP);
@@ -45,6 +47,16 @@ public class XMLTokener extends JSONTokener {
super(s);
}
/**
* Construct an XMLTokener from a Reader and an XMLParserConfiguration.
* @param r A source reader.
* @param configuration the configuration that can be used to set certain flags
*/
public XMLTokener(Reader r, XMLParserConfiguration configuration) {
super(r);
this.configuration = configuration;
}
/**
* Get the text in the CDATA block.
* @return The string up to the <code>]]&gt;</code>.
@@ -83,7 +95,7 @@ public class XMLTokener extends JSONTokener {
StringBuilder sb;
do {
c = next();
} while (Character.isWhitespace(c));
} while (Character.isWhitespace(c) && configuration.shouldTrimWhiteSpace());
if (c == 0) {
return null;
}
@@ -97,7 +109,9 @@ public class XMLTokener extends JSONTokener {
}
if (c == '<') {
back();
return sb.toString().trim();
if (configuration.shouldTrimWhiteSpace()) {
return sb.toString().trim();
} else return sb.toString();
}
if (c == '&') {
sb.append(nextEntity(c));

View File

@@ -0,0 +1,174 @@
package org.json;
import org.junit.Test;
import java.math.BigDecimal;
import java.math.BigInteger;
import static org.junit.Assert.*;
public class NumberConversionUtilTest {
@Test
public void shouldParseDecimalFractionNumbersWithMultipleLeadingZeros(){
Number number = NumberConversionUtil.stringToNumber("00.10d");
assertEquals("Do not match", 0.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", 0.10f, number.floatValue(),0.0f);
assertEquals("Do not match", 0, number.longValue(),0);
assertEquals("Do not match", 0, number.intValue(),0);
}
@Test
public void shouldParseDecimalFractionNumbersWithSingleLeadingZero(){
Number number = NumberConversionUtil.stringToNumber("0.10d");
assertEquals("Do not match", 0.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", 0.10f, number.floatValue(),0.0f);
assertEquals("Do not match", 0, number.longValue(),0);
assertEquals("Do not match", 0, number.intValue(),0);
}
@Test
public void shouldParseDecimalFractionNumbersWithZerosAfterDecimalPoint(){
Number number = NumberConversionUtil.stringToNumber("0.010d");
assertEquals("Do not match", 0.010d, number.doubleValue(),0.0d);
assertEquals("Do not match", 0.010f, number.floatValue(),0.0f);
assertEquals("Do not match", 0, number.longValue(),0);
assertEquals("Do not match", 0, number.intValue(),0);
}
@Test
public void shouldParseMixedDecimalFractionNumbersWithMultipleLeadingZeros(){
Number number = NumberConversionUtil.stringToNumber("00200.10d");
assertEquals("Do not match", 200.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", 200.10f, number.floatValue(),0.0f);
assertEquals("Do not match", 200, number.longValue(),0);
assertEquals("Do not match", 200, number.intValue(),0);
}
@Test
public void shouldParseMixedDecimalFractionNumbersWithoutLeadingZero(){
Number number = NumberConversionUtil.stringToNumber("200.10d");
assertEquals("Do not match", 200.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", 200.10f, number.floatValue(),0.0f);
assertEquals("Do not match", 200, number.longValue(),0);
assertEquals("Do not match", 200, number.intValue(),0);
}
@Test
public void shouldParseMixedDecimalFractionNumbersWithZerosAfterDecimalPoint(){
Number number = NumberConversionUtil.stringToNumber("200.010d");
assertEquals("Do not match", 200.010d, number.doubleValue(),0.0d);
assertEquals("Do not match", 200.010f, number.floatValue(),0.0f);
assertEquals("Do not match", 200, number.longValue(),0);
assertEquals("Do not match", 200, number.intValue(),0);
}
@Test
public void shouldParseNegativeDecimalFractionNumbersWithMultipleLeadingZeros(){
Number number = NumberConversionUtil.stringToNumber("-00.10d");
assertEquals("Do not match", -0.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", -0.10f, number.floatValue(),0.0f);
assertEquals("Do not match", -0, number.longValue(),0);
assertEquals("Do not match", -0, number.intValue(),0);
}
@Test
public void shouldParseNegativeDecimalFractionNumbersWithSingleLeadingZero(){
Number number = NumberConversionUtil.stringToNumber("-0.10d");
assertEquals("Do not match", -0.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", -0.10f, number.floatValue(),0.0f);
assertEquals("Do not match", -0, number.longValue(),0);
assertEquals("Do not match", -0, number.intValue(),0);
}
@Test
public void shouldParseNegativeDecimalFractionNumbersWithZerosAfterDecimalPoint(){
Number number = NumberConversionUtil.stringToNumber("-0.010d");
assertEquals("Do not match", -0.010d, number.doubleValue(),0.0d);
assertEquals("Do not match", -0.010f, number.floatValue(),0.0f);
assertEquals("Do not match", -0, number.longValue(),0);
assertEquals("Do not match", -0, number.intValue(),0);
}
@Test
public void shouldParseNegativeMixedDecimalFractionNumbersWithMultipleLeadingZeros(){
Number number = NumberConversionUtil.stringToNumber("-00200.10d");
assertEquals("Do not match", -200.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", -200.10f, number.floatValue(),0.0f);
assertEquals("Do not match", -200, number.longValue(),0);
assertEquals("Do not match", -200, number.intValue(),0);
}
@Test
public void shouldParseNegativeMixedDecimalFractionNumbersWithoutLeadingZero(){
Number number = NumberConversionUtil.stringToNumber("-200.10d");
assertEquals("Do not match", -200.10d, number.doubleValue(),0.0d);
assertEquals("Do not match", -200.10f, number.floatValue(),0.0f);
assertEquals("Do not match", -200, number.longValue(),0);
assertEquals("Do not match", -200, number.intValue(),0);
}
@Test
public void shouldParseNegativeMixedDecimalFractionNumbersWithZerosAfterDecimalPoint(){
Number number = NumberConversionUtil.stringToNumber("-200.010d");
assertEquals("Do not match", -200.010d, number.doubleValue(),0.0d);
assertEquals("Do not match", -200.010f, number.floatValue(),0.0f);
assertEquals("Do not match", -200, number.longValue(),0);
assertEquals("Do not match", -200, number.intValue(),0);
}
@Test
public void shouldParseNumbersWithExponents(){
Number number = NumberConversionUtil.stringToNumber("23.45e7");
assertEquals("Do not match", 23.45e7d, number.doubleValue(),0.0d);
assertEquals("Do not match", 23.45e7f, number.floatValue(),0.0f);
assertEquals("Do not match", 2.345E8, number.longValue(),0);
assertEquals("Do not match", 2.345E8, number.intValue(),0);
}
@Test
public void shouldParseNegativeNumbersWithExponents(){
Number number = NumberConversionUtil.stringToNumber("-23.45e7");
assertEquals("Do not match", -23.45e7d, number.doubleValue(),0.0d);
assertEquals("Do not match", -23.45e7f, number.floatValue(),0.0f);
assertEquals("Do not match", -2.345E8, number.longValue(),0);
assertEquals("Do not match", -2.345E8, number.intValue(),0);
}
@Test
public void shouldParseBigDecimal(){
Number number = NumberConversionUtil.stringToNumber("19007199254740993.35481234487103587486413587843213584");
assertTrue(number instanceof BigDecimal);
}
@Test
public void shouldParseBigInteger(){
Number number = NumberConversionUtil.stringToNumber("1900719925474099335481234487103587486413587843213584");
assertTrue(number instanceof BigInteger);
}
@Test
public void shouldIdentifyPotentialNumber(){
assertTrue("Does not identify as number", NumberConversionUtil.potentialNumber("112.123"));
assertTrue("Does not identify as number", NumberConversionUtil.potentialNumber("112e123"));
assertTrue("Does not identify as number", NumberConversionUtil.potentialNumber("-112.123"));
assertTrue("Does not identify as number", NumberConversionUtil.potentialNumber("-112e23"));
assertFalse("Does not identify as not number", NumberConversionUtil.potentialNumber("--112.123"));
assertFalse("Does not identify as not number", NumberConversionUtil.potentialNumber("-a112.123"));
assertFalse("Does not identify as not number", NumberConversionUtil.potentialNumber("a112.123"));
assertFalse("Does not identify as not number", NumberConversionUtil.potentialNumber("e112.123"));
}
@Test(expected = NumberFormatException.class)
public void shouldExpectExceptionWhenNumberIsNotFormatted(){
NumberConversionUtil.stringToNumber("112.aa123");
}
}

View File

@@ -28,10 +28,13 @@ import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONParserConfiguration;
import org.json.JSONPointerException;
import org.json.JSONString;
import org.json.JSONTokener;
import org.json.ParserConfiguration;
import org.json.junit.data.MyJsonString;
import org.junit.Ignore;
import org.junit.Test;
import com.jayway.jsonpath.Configuration;
@@ -1384,6 +1387,7 @@ public class JSONArrayTest {
/**
* Tests for stack overflow. See https://github.com/stleary/JSON-java/issues/654
*/
@Ignore("This test relies on system constraints and may not always pass. See: https://github.com/stleary/JSON-java/issues/821")
@Test(expected = JSONException.class)
public void issue654StackOverflowInputWellFormed() {
//String input = new String(java.util.Base64.getDecoder().decode(base64Bytes));
@@ -1391,7 +1395,7 @@ public class JSONArrayTest {
JSONTokener tokener = new JSONTokener(resourceAsStream);
JSONArray json_input = new JSONArray(tokener);
assertNotNull(json_input);
fail("Excepected Exception.");
fail("Excepected Exception due to stack overflow.");
Util.checkJSONArrayMaps(json_input);
}
@@ -1415,4 +1419,92 @@ public class JSONArrayTest {
.put(2);
assertFalse(ja1.similar(ja3));
}
@Test(expected = JSONException.class)
public void testRecursiveDepth() {
HashMap<String, Object> map = new HashMap<>();
map.put("t", map);
new JSONArray().put(map);
}
@Test(expected = JSONException.class)
public void testRecursiveDepthAtPosition() {
HashMap<String, Object> map = new HashMap<>();
map.put("t", map);
new JSONArray().put(0, map);
}
@Test(expected = JSONException.class)
public void testRecursiveDepthArray() {
ArrayList<Object> array = new ArrayList<>();
array.add(array);
new JSONArray(array);
}
@Test
public void testRecursiveDepthAtPositionDefaultObject() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray().put(0, map);
}
@Test
public void testRecursiveDepthAtPosition1000Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1000);
new JSONArray().put(0, map, new JSONParserConfiguration().withMaxNestingDepth(1000));
}
@Test(expected = JSONException.class)
public void testRecursiveDepthAtPosition1001Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1001);
new JSONArray().put(0, map);
}
@Test(expected = JSONException.class)
public void testRecursiveDepthArrayLimitedMaps() {
ArrayList<Object> array = new ArrayList<>();
array.add(array);
new JSONArray(array);
}
@Test
public void testRecursiveDepthArrayForDefaultLevels() {
ArrayList<Object> array = buildNestedArray(ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray(array, new JSONParserConfiguration());
}
@Test
public void testRecursiveDepthArrayFor1000Levels() {
try {
ArrayList<Object> array = buildNestedArray(1000);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
new JSONArray(array, parserConfiguration);
} catch (StackOverflowError e) {
String javaVersion = System.getProperty("java.version");
if (javaVersion.startsWith("11.")) {
System.out.println(
"testRecursiveDepthArrayFor1000Levels() allowing intermittent stackoverflow, Java Version: "
+ javaVersion);
} else {
String errorStr = "testRecursiveDepthArrayFor1000Levels() unexpected stackoverflow, Java Version: "
+ javaVersion;
System.out.println(errorStr);
throw new RuntimeException(errorStr);
}
}
}
@Test(expected = JSONException.class)
public void testRecursiveDepthArrayFor1001Levels() {
ArrayList<Object> array = buildNestedArray(1001);
new JSONArray(array);
}
public static ArrayList<Object> buildNestedArray(int maxDepth) {
if (maxDepth <= 0) {
return new ArrayList<>();
}
ArrayList<Object> nestedArray = new ArrayList<>();
nestedArray.add(buildNestedArray(maxDepth - 1));
return nestedArray;
}
}

View File

@@ -709,7 +709,7 @@ public class JSONMLTest {
@Test
public void testToJSONArray_jsonOutput() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
final String expectedJsonString = "[\"root\",[\"id\",\"01\"],[\"id\",1],[\"id\",\"00\"],[\"id\",0],[\"item\",{\"id\":\"01\"}],[\"title\",true]]";
final String expectedJsonString = "[\"root\",[\"id\",1],[\"id\",1],[\"id\",0],[\"id\",0],[\"item\",{\"id\":1}],[\"title\",true]]";
final JSONArray actualJsonOutput = JSONML.toJSONArray(originalXml, false);
assertEquals(expectedJsonString, actualJsonOutput.toString());
}
@@ -762,8 +762,8 @@ public class JSONMLTest {
final String xml = JSONML.toString(originalObject);
final JSONObject revertedObject = JSONML.toJSONObject(xml, false);
final String newJson = revertedObject.toString();
assertTrue("JSON Objects are not similar",originalObject.similar(revertedObject));
assertEquals("original JSON does not equal the new JSON",originalJson, newJson);
assertTrue("JSON Objects are not similar", originalObject.similar(revertedObject));
assertTrue("JSON Strings are not similar", new JSONObject(originalJson).similar(new JSONObject(newJson)));
}
// these tests do not pass for the following reasons:

View File

@@ -32,8 +32,10 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONPointerException;
import org.json.JSONParserConfiguration;
import org.json.JSONString;
import org.json.JSONTokener;
import org.json.ParserConfiguration;
import org.json.XML;
import org.json.junit.data.BrokenToString;
import org.json.junit.data.ExceptionalBean;
@@ -55,6 +57,7 @@ import org.json.junit.data.RecursiveBeanEquals;
import org.json.junit.data.Singleton;
import org.json.junit.data.SingletonEnum;
import org.json.junit.data.WeirdList;
import org.junit.Ignore;
import org.junit.Test;
import com.jayway.jsonpath.Configuration;
@@ -2095,7 +2098,9 @@ public class JSONObjectTest {
"}";
JSONObject jsonObject = new JSONObject(jsonObjectStr);
assertTrue("jsonObject valueToString() incorrect",
JSONObject.valueToString(jsonObject).equals(jsonObject.toString()));
new JSONObject(JSONObject.valueToString(jsonObject))
.similar(new JSONObject(jsonObject.toString()))
);
String jsonArrayStr =
"[1,2,3]";
JSONArray jsonArray = new JSONArray(jsonArrayStr);
@@ -2106,7 +2111,8 @@ public class JSONObjectTest {
map.put("key2", "val2");
map.put("key3", "val3");
assertTrue("map valueToString() incorrect",
jsonObject.toString().equals(JSONObject.valueToString(map)));
new JSONObject(jsonObject.toString())
.similar(new JSONObject(JSONObject.valueToString(map))));
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(Integer.valueOf(1));
collection.add(Integer.valueOf(2));
@@ -3662,6 +3668,7 @@ public class JSONObjectTest {
/**
* Tests for stack overflow. See https://github.com/stleary/JSON-java/issues/654
*/
@Ignore("This test relies on system constraints and may not always pass. See: https://github.com/stleary/JSON-java/issues/821")
@Test(expected = JSONException.class)
public void issue654StackOverflowInputWellFormed() {
//String input = new String(java.util.Base64.getDecoder().decode(base64Bytes));
@@ -3669,7 +3676,7 @@ public class JSONObjectTest {
JSONTokener tokener = new JSONTokener(resourceAsStream);
JSONObject json_input = new JSONObject(tokener);
assertNotNull(json_input);
fail("Excepected Exception.");
fail("Excepected Exception due to stack overflow.");
}
@Test
@@ -3713,4 +3720,101 @@ public class JSONObjectTest {
assertThrows(JSONException.class, () -> new JSONObject(bean));
}
}
@Test(expected = JSONException.class)
public void issue743SerializationMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("t", map);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}
@Test(expected = JSONException.class)
public void testCircularReferenceMultipleLevel() {
HashMap<String, Object> inside = new HashMap<>();
HashMap<String, Object> jsonObject = new HashMap<>();
inside.put("inside", jsonObject);
jsonObject.put("test", inside);
new JSONObject(jsonObject);
}
@Test
public void issue743SerializationMapWith512Objects() {
HashMap<String, Object> map = buildNestedMap(ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}
@Test
public void issue743SerializationMapWith1000Objects() {
HashMap<String, Object> map = buildNestedMap(1000);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
JSONObject object = new JSONObject(map, parserConfiguration);
String jsonString = object.toString();
}
@Test(expected = JSONException.class)
public void issue743SerializationMapWith1001Objects() {
HashMap<String, Object> map = buildNestedMap(1001);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}
@Test(expected = JSONException.class)
public void testCircleReferenceFirstLevel() {
Map<Object, Object> jsonObject = new HashMap<>();
jsonObject.put("test", jsonObject);
new JSONObject(jsonObject, new JSONParserConfiguration());
}
@Test(expected = StackOverflowError.class)
public void testCircleReferenceMultiplyLevel_notConfigured_expectedStackOverflow() {
Map<Object, Object> inside = new HashMap<>();
Map<Object, Object> jsonObject = new HashMap<>();
inside.put("test", jsonObject);
jsonObject.put("test", inside);
new JSONObject(jsonObject, new JSONParserConfiguration().withMaxNestingDepth(99999));
}
@Test(expected = JSONException.class)
public void testCircleReferenceMultiplyLevel_configured_expectedJSONException() {
Map<Object, Object> inside = new HashMap<>();
Map<Object, Object> jsonObject = new HashMap<>();
inside.put("test", jsonObject);
jsonObject.put("test", inside);
new JSONObject(jsonObject, new JSONParserConfiguration());
}
@Test
public void testDifferentKeySameInstanceNotACircleReference() {
Map<Object, Object> map1 = new HashMap<>();
Map<Object, Object> map2 = new HashMap<>();
map1.put("test1", map2);
map1.put("test2", map2);
new JSONObject(map1);
}
/**
* Method to build nested map of max maxDepth
*
* @param maxDepth
* @return
*/
public static HashMap<String, Object> buildNestedMap(int maxDepth) {
if (maxDepth <= 0) {
return new HashMap<>();
}
HashMap<String, Object> nestedMap = new HashMap<>();
nestedMap.put("t", buildNestedMap(maxDepth - 1));
return nestedMap;
}
}

View File

@@ -4,11 +4,6 @@ package org.json.junit;
Public Domain.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
@@ -27,6 +22,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static org.junit.Assert.*;
/**
* Tests for JSON-Java XML.java with XMLParserConfiguration.java
@@ -557,6 +554,37 @@ public class XMLConfigurationTest {
assertEquals(actualXML, resultXML);
}
@Test
public void shouldHandleEmptyNodeValue()
{
JSONObject inputJSON = new JSONObject();
inputJSON.put("Emptyness", "");
String expectedXmlWithoutExplicitEndTag = "<Emptyness/>";
String expectedXmlWithExplicitEndTag = "<Emptyness></Emptyness>";
assertEquals(expectedXmlWithoutExplicitEndTag, XML.toString(inputJSON, null,
new XMLParserConfiguration().withCloseEmptyTag(false)));
assertEquals(expectedXmlWithExplicitEndTag, XML.toString(inputJSON, null,
new XMLParserConfiguration().withCloseEmptyTag(true)));
}
@Test
public void shouldKeepConfigurationIntactAndUpdateCloseEmptyTagChoice()
{
XMLParserConfiguration keepStrings = XMLParserConfiguration.KEEP_STRINGS;
XMLParserConfiguration keepStringsAndCloseEmptyTag = keepStrings.withCloseEmptyTag(true);
XMLParserConfiguration keepDigits = keepStringsAndCloseEmptyTag.withKeepStrings(false);
XMLParserConfiguration keepDigitsAndNoCloseEmptyTag = keepDigits.withCloseEmptyTag(false);
assertTrue(keepStrings.isKeepStrings());
assertFalse(keepStrings.isCloseEmptyTag());
assertTrue(keepStringsAndCloseEmptyTag.isKeepStrings());
assertTrue(keepStringsAndCloseEmptyTag.isCloseEmptyTag());
assertFalse(keepDigits.isKeepStrings());
assertTrue(keepDigits.isCloseEmptyTag());
assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepStrings());
assertFalse(keepDigitsAndNoCloseEmptyTag.isCloseEmptyTag());
}
/**
* Investigate exactly how the "content" keyword works
*/
@@ -733,7 +761,7 @@ public class XMLConfigurationTest {
@Test
public void testToJSONArray_jsonOutput() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",1,\"00\",0],\"title\":true}}");
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":1},\"id\":[1,1,0,0],\"title\":true}}");
final JSONObject actualJsonOutput = XML.toJSONObject(originalXml,
new XMLParserConfiguration().withKeepStrings(false));
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expected);
@@ -1153,4 +1181,4 @@ public class XMLConfigurationTest {
assertTrue("Error: " +e.getMessage(), false);
}
}
}
}

View File

@@ -791,7 +791,7 @@ public class XMLTest {
@Test
public void testToJSONArray_jsonOutput() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
final JSONObject expectedJson = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",1,\"00\",0],\"title\":true}}");
final JSONObject expectedJson = new JSONObject("{\"root\":{\"item\":{\"id\":1},\"id\":[1,1,0,0],\"title\":true}}");
final JSONObject actualJsonOutput = XML.toJSONObject(originalXml, false);
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expectedJson);
@@ -1177,6 +1177,30 @@ public class XMLTest {
}
@Test
public void shouldCreateExplicitEndTagWithEmptyValueWhenConfigured(){
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
JSONObject jsonObject = new JSONObject(jsonString);
String expectedXmlString = "<encloser><outer><innerOne></innerOne><innerTwo>two</innerTwo></outer></encloser>";
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(true));
JSONObject actualJsonObject = XML.toJSONObject(xmlForm);
JSONObject expectedJsonObject = XML.toJSONObject(expectedXmlString);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void shouldNotCreateExplicitEndTagWithEmptyValueWhenNotConfigured(){
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
JSONObject jsonObject = new JSONObject(jsonString);
String expectedXmlString = "<encloser><outer><innerOne/><innerTwo>two</innerTwo></outer></encloser>";
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(false));
JSONObject actualJsonObject = XML.toJSONObject(xmlForm);
JSONObject expectedJsonObject = XML.toJSONObject(expectedXmlString);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void testIndentSimpleJsonObject(){
String str = "{ \"employee\": { \n" +
@@ -1234,7 +1258,7 @@ public class XMLTest {
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
expected.append(buffer, 0, numRead);
}
assertEquals(expected.toString(), actualString);
assertTrue(XML.toJSONObject(expected.toString()).similar(XML.toJSONObject(actualString)));
}
} catch (IOException e) {
fail("file writer error: " +e.getMessage());
@@ -1299,6 +1323,80 @@ public class XMLTest {
"parameter of the XMLParserConfiguration used");
}
}
@Test
public void testWithWhitespaceTrimmingDisabled() {
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false));
String expectedJsonString = "{\"testXml\":\" Test Whitespace String \t \"}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void testNestedWithWhitespaceTrimmingDisabled() {
String originalXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses>\n"+
" <address>\n"+
" <name> Sherlock Holmes </name>\n"+
" </address>\n"+
"</addresses>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false));
String expectedJsonString = "{\"addresses\":{\"address\":{\"name\":\" Sherlock Holmes \"}}}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void shouldTrimWhitespaceDoesNotSupportTagsEqualingCDataTagName() {
// When using withShouldTrimWhitespace = true, input containing tags with same name as cDataTagName is unsupported and should not be used in conjunction
String originalXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses>\n"+
" <address>\n"+
" <content> Sherlock Holmes </content>\n"+
" </address>\n"+
"</addresses>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false).withcDataTagName("content"));
String expectedJsonString = "{\"addresses\":{\"address\":[[\"\\n \",\" Sherlock Holmes \",\"\\n \"]]}}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void shouldTrimWhitespaceEnabledDropsTagsEqualingCDataTagNameButValueRemains() {
String originalXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses>\n"+
" <address>\n"+
" <content> Sherlock Holmes </content>\n"+
" </address>\n"+
"</addresses>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(true).withcDataTagName("content"));
String expectedJsonString = "{\"addresses\":{\"address\":\"Sherlock Holmes\"}}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void testWithWhitespaceTrimmingEnabled() {
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(true));
String expectedJsonString = "{\"testXml\":\"Test Whitespace String\"}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void testWithWhitespaceTrimmingEnabledByDefault() {
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration());
String expectedJsonString = "{\"testXml\":\"Test Whitespace String\"}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
}

View File

@@ -12,7 +12,7 @@ import java.util.List;
*/
public class WeirdList {
/** */
private final List<Integer> list = new ArrayList();
private final List<Integer> list = new ArrayList<>();
/**
* @param vals
@@ -25,14 +25,14 @@ public class WeirdList {
* @return a copy of the list
*/
public List<Integer> get() {
return new ArrayList(this.list);
return new ArrayList<>(this.list);
}
/**
* @return a copy of the list
*/
public List<Integer> getALL() {
return new ArrayList(this.list);
return new ArrayList<>(this.list);
}
/**