Compare commits

152 Commits

Author SHA1 Message Date
Sean Leary
e635f40238 Merge pull request #1027 from Simulant87/1023-set-default-locale
Save/restore default locale in test
2025-12-29 19:42:57 -06:00
Sean Leary
d5e744ca90 Merge pull request #1028 from Simulant87/fix-sonarqube-reliability-issues
Refactoring: Fix sonarqube reliability issues
2025-12-29 19:42:02 -06:00
Sean Leary
e0c4086168 Merge pull request #1029 from Simulant87/external-javadoc-badge
add badge to external hosted javadoc
2025-12-29 19:41:31 -06:00
Sean Leary
cf653682be Merge pull request #1030 from stleary/pre-release-20251224
pre-release-20251224 Prep for next release
2025-12-24 09:16:47 -06:00
Sean Leary
24bba97c1d pre-release-20251224 update docs and builds for next release 2025-12-24 09:05:18 -06:00
Simulant
96353de304 add badge to external hosted javadoc 2025-12-21 23:16:01 +01:00
Simulant
8cbb4d5bb3 Fix sonarqube reliability issues 2025-12-20 22:57:24 +01:00
Simulant
421abfdc1f save and restore the current default locale, to avoid any side effects on other executions in the same JVM 2025-12-20 22:27:45 +01:00
Sean Leary
128fb42ccc Merge pull request #1021 from Simulant87/update-build-script
Update github build actions, add LTS JDK 25 build
2025-11-18 07:26:35 -06:00
Simulant87
f8e6dfdc63 Merge pull request #3 from Simulant87/update-readme
Update README.md tested on java 25
2025-11-14 15:49:30 +01:00
Simulant87
3bc98dfc7f Update README.md tested on java 25 2025-11-14 15:49:09 +01:00
Simulant87
005dc7b49e add build for LTS JDK 25 2025-11-14 15:47:58 +01:00
Simulant87
d38cb064fd reset setup-java to version 1 for 1.6 build 2025-11-14 15:45:41 +01:00
Simulant87
e9a7d7c72e add distribution to java 1.6 build 2025-11-14 15:40:21 +01:00
Simulant87
73c582e129 update github actions to version 5
consistently update all actions checkout, setup-java, upload-artifactory to version 5
2025-11-14 15:29:52 +01:00
Sean Leary
a6ca84074a Merge pull request #1020 from Abhineshhh/fix/support-java-records
Fix: Support Java record accessors in JSONObject
2025-11-11 20:19:42 -06:00
Sean Leary
8c14e96c44 Merge pull request #1017 from Md-Yasir/enhancement/refactors
Code Refactors
2025-11-09 19:02:49 -06:00
AbhineshJha
8f3b0f1c13 Add runtime record detection for backward compatibility 2025-11-02 22:32:44 +05:30
AbhineshJha
f2acf8af69 Optimize method name exclusion using Set lookup instead of multiple equals checks 2025-11-01 19:33:29 +05:30
AbhineshJha
fd1eee9c3b Add comprehensive edge case tests for record support 2025-11-01 19:33:29 +05:30
AbhineshJha
2550c692cf Refactor: Extract isRecordStyleAccessor helper method 2025-11-01 19:33:29 +05:30
AbhineshJha
20f5200000 Fix: Support Java record accessors in JSONObject 2025-11-01 19:33:29 +05:30
Sean Leary
25f355a953 Merge pull request #1006 from sk02241994/feature-1003
1003: Implement JSONObject.fromJson() with unit tests
2025-10-31 11:25:03 -05:00
sk02241994
42800c208a Updating to work with java 1.6 2025-10-28 13:06:11 +11:00
md-yasir
0cdc5e5170 Reverted Constructor access to public 2025-10-25 20:51:50 +05:30
md-yasir
ac65ee0490 Revert "Refactored stop conditions to be invariant by using while loop."
This issue can be ignored
2025-10-25 20:37:54 +05:30
md-yasir
39e8ead7cd Added java doc for deprecated decoration 2025-10-24 09:37:46 +05:30
md-yasir
6dd878d3c9 Deprecated public constructors instead of making it private. 2025-10-24 09:10:53 +05:30
md-yasir
2c6082a0a2 Refactored stop conditions to be invariant by using while loop. 2025-10-23 22:50:12 +05:30
md-yasir
5dc1031d17 Made JSONMl constructor to private and refactored ternary operations to independent statement in L243 2025-10-23 22:38:01 +05:30
md-yasir
1de42aa4fd Made CookieList constructor to private. 2025-10-23 22:37:00 +05:30
md-yasir
c13b57ca26 Made Cookie constructor to private. 2025-10-23 22:36:53 +05:30
sk02241994
f92f281620 Updating to work with java 1.6 2025-10-23 17:33:37 +11:00
sk02241994
8ccf5d7525 Removing the interface classes and simplifying the implementation to use if else instead 2025-10-23 17:32:07 +11:00
sk02241994
a7c193090a Updating docs 2025-10-16 14:23:30 +11:00
sk02241994
c4c2beb874 Limiting implemetation by removing the new classes. 2025-10-16 14:19:19 +11:00
sk02241994
9adea9e12d Updating to work with java 1.6 2025-10-13 12:39:15 +11:00
sk02241994
7465da858c - Updating for java 1.6
- Resolving Sonar cube issues.
2025-10-13 12:39:15 +11:00
sk02241994
0521928463 - Added implementation for Enum and Map
- Moving the CustomClass to data folder.
- Removing JSONBuilder.java
- Moving the implementation of JSONBuilder to JSONObject.
2025-10-13 12:39:14 +11:00
sk02241994
fbb6b3158e Updating to work with java 1.6 2025-10-13 12:39:14 +11:00
sk02241994
ebc13d6685 Updating to work with java 1.6 2025-10-13 12:39:13 +11:00
sk02241994
7d28955216 Updating to work with java 1.6 2025-10-13 12:39:13 +11:00
sk02241994
83a0e34be5 1003: Implement JSONObject.fromJson() with unit tests 2025-10-13 12:39:12 +11:00
Sean Leary
3e8d1d119f Merge pull request #1014 from Md-Yasir/enhancement/string-check
changed string checking logic
2025-10-11 21:02:44 -05:00
md-yasir
1a2c50b40c changed string checking logic >> string.length() > 0 to !string.isEmpty() 2025-10-11 19:48:33 +05:30
Sean Leary
eb97037f7c Merge pull request #1013 from marilynel/master
sonarqube changes to jsonarray
2025-09-24 12:38:56 -05:00
marilynel
05867c4b0b Merge branch 'master' of https://github.com/marilynel/JSON-java 2025-09-21 16:37:20 -08:00
marilynel
c6efa080c0 more cleanup sonarqube JSONArray 2025-09-21 16:36:52 -08:00
Sean Leary
aff59d06fa Merge pull request #1011 from marilynel/master
more sonarcube fixes
2025-09-18 20:13:43 -05:00
Sean Leary
b258ea3d46 Merge pull request #1008 from eleumik/eleumik-patch-1007-array
Update JSONArray.java for #1007
2025-09-18 20:12:55 -05:00
Sean Leary
a5e234aa19 Merge pull request #1009 from eleumik/eleumik-patch-1
Update JSONTokener.java for #1007
2025-09-18 20:12:03 -05:00
marilynel
f2af220cb4 more sonarcube fixes 2025-09-14 10:59:39 -08:00
Sean Leary
a3edc1da0f Merge pull request #1005 from marilynel/master
fixing sonar cube issues
2025-09-11 15:42:48 -05:00
Michele Vivoda
686c084897 Update JSONTokener.java for #1007
fixed parse of `0.` in strict mode
2025-09-10 02:30:19 +02:00
Michele Vivoda
9de3005566 Update JSONArray.java for #1007
fix array content starting with ',' in strict mode
2025-09-10 02:21:16 +02:00
marilynel
69c87dc4db more sonarcube optimization in jsonobject.java 2025-09-07 12:52:59 -08:00
marilynel
53cfa742a7 more sonarcube optimization in jsonobject.java 2025-09-07 12:41:37 -08:00
marilynel
4e0f62b1a6 more sonarcube optimization in jsonobject.java 2025-09-07 12:28:52 -08:00
Sean Leary
9b8eefc2de Merge pull request #1004 from marilynel/master
sonarcube cleanup in JSONObject; more to do
2025-08-29 07:49:28 -05:00
marilynel
6ed2880f55 more sonarcube cleanup 2025-08-24 12:55:49 -08:00
marilynel
9bb26bdb34 sonar cube stuff 2025-08-03 11:52:20 -08:00
Sean Leary
78137d389d Merge pull request #1001 from marilynel/master
addressing sonarqube concerns in JSONObject
2025-07-31 20:54:08 -05:00
marilynel
38c3a0bb3f more sonarcube issues 2025-07-27 11:45:07 -08:00
marilynel
ebd9a17a3b addressing minor sonarqube concerns 2025-07-27 11:26:50 -08:00
Sean Leary
82432f0245 Merge pull request #1000 from marilynel/master
fixing sonarcube issues
2025-07-23 20:46:33 -05:00
marilynel
e762629bcc oops one more sonarcube issue lol 2025-07-20 12:04:51 -08:00
marilynel
7fc41a6c0e addressing cognitive complextity 2025-07-20 11:58:30 -08:00
marilynel
d5d82cdb87 fixing sonarcube issues 2025-07-20 11:31:29 -08:00
Sean Leary
0a9364e920 Merge pull request #999 from marilynel/master
fixed some strict mode issues
2025-07-16 20:12:57 -05:00
marilynel
c91b728386 oops forgot null 2025-07-13 12:52:42 -08:00
marilynel
fdaeb486ed fixed some strict mode issues 980 2025-07-13 12:41:17 -08:00
Sean Leary
f0a78aff61 Merge pull request #995 from marilynel/master
Fix regression XML parsing null with keepStrings
2025-07-09 20:18:48 -05:00
Sean Leary
a79e8a15e5 Merge pull request #994 from stleary/tech-debt-20250701
tech-debt-25250701
2025-07-08 11:04:29 -05:00
marilynel
7bb3df8ebf added test details 2025-07-06 12:41:44 -08:00
marilynel
3dce55794f fixed keeping null as string 2025-07-06 12:37:05 -08:00
Sean Leary
d7593fb808 Merge pull request #992 from surajdm123/add-tests
Added JUnit test cases for HTTPTokener
2025-07-06 08:27:59 -05:00
Sean Leary
1eed44a59e Merge pull request #993 from surajdm123/add-tests-2
Added JUnit tests for XMLTokenerTest
2025-07-06 08:27:20 -05:00
Sean Leary
7eccadefcd Merge pull request #991 from Simulant87/update-codeql-v3
update CodeQL to v3
2025-07-04 16:39:18 -05:00
Sean Leary
7b0d1942b4 tech-debt-25250701 add jacoco to gradle build, refactor JSONObject to restore performance 2025-07-03 20:39:13 -05:00
surajdm123
a729c2077a Added JUnit tests for XMLTokenerTest 2025-07-03 01:23:46 -07:00
surajdm123
7ac773be72 Added JUnit test cases for HTTPTokener 2025-07-03 00:58:15 -07:00
Simulant
7da120e631 update CodeQL to v3 2025-07-01 22:57:36 +02:00
Sean Leary
197afddbfb Merge pull request #990 from Simulant87/984-refactor-cognitive-complexity-populateMap
Refactor JSONObject populateMap() per SonarQube
2025-07-01 07:13:18 -05:00
Sean Leary
1bdaacc8b0 Merge pull request #989 from AlexCai2019/master
Minor refactoring
2025-06-27 20:17:17 -05:00
Alex Cai
c882783d58 Format line 2755 in JSONObject.java 2025-06-27 01:44:27 +08:00
Simulant
5063d314a5 #984 extract method for annotation value check 2025-06-25 23:08:01 +02:00
Simulant
916fba5d39 #984 extract methods reducing cognitive complexity
for JSONObject#populateMap
2025-06-25 23:00:07 +02:00
AlexCai2019
aac376f305 Remove a redundant condition and an empty string
Remove "NULL.equals(object)" on line 2756 of JSONObject.java since line 2752 has already tested it.
Remove the empty string on line 249 of JSONPointer.java.
2025-06-23 01:31:51 +08:00
Sean Leary
32e56da786 Merge pull request #988 from stleary/remove-unused-code-jsonobject
removed unused method from jsonobject
2025-06-16 11:34:35 -05:00
Sean Leary
50330430ce remove-unused-code-jsonobject removed unused method from jsonobject 2025-06-07 16:15:43 -05:00
Sean Leary
f1935f5254 Merge pull request #987 from AlexCai2019/master
Use constant.equals()
2025-06-07 09:59:48 -05:00
AlexCai2019
e800cc349f Use constant.equals()
There are some equals() that are not constant.equals(variable), but variable.equals(constant)
2025-06-05 02:15:49 +08:00
Sean Leary
72a1a48173 Merge pull request #983 from harshith8854/master
Use JSONParserConfiguration to decide on serializing Null fields into JSONObject #982
2025-05-31 09:58:46 -05:00
hboggavarapu
a381060f81 Add testcase to assert Null fields serialization without JSONParserConfiguration 2025-05-24 21:54:12 +05:30
hboggavarapu
dadc3e59dc Use JSONParserConfiguration to decide on serializing null fields into JSONObject #982 2025-05-23 17:57:08 +05:30
Sean Leary
24fafcffeb Merge pull request #981 from stleary/pre-release-20250517
pre-release-20250517 prep for next release
2025-05-17 07:44:38 -05:00
Sean Leary
418d5e9973 pre-release-20250517 prep for next release 2025-05-17 07:41:21 -05:00
Sean Leary
82a02d879e Merge pull request #969 from marilynel/master
refactored large test for strict mode
2025-04-18 11:48:25 -05:00
marilynel
2184ef34d1 refactored large test for strict mode 2025-04-13 11:35:45 -07:00
Sean Leary
8e65eaa992 Merge pull request #968 from marilynel/master
Update keepStrings behavior to reflect changes in keepBooleanAsString, keepNumberAsString
2025-04-10 11:56:36 -05:00
marilynel
74439cf696 Merge branch 'master' of https://github.com/marilynel/JSON-java 2025-04-06 11:05:02 -07:00
marilynel
53da5ce2a9 adjusted keepstrings behavior to reflect changes in keepBooleanAsString & keepNumberAsString 2025-04-06 11:04:33 -07:00
Sean Leary
2e9ad6ff5a Merge pull request #966 from marilynel/master
granular flags to control for keeping boolean or number values as strings
2025-04-04 07:40:55 -05:00
marilynel
8dbf03e76b work on issue 841 2025-03-30 12:21:44 -07:00
Sean Leary
4917e3579d Merge pull request #962 from marilynel/master
Fix: handles edge case 'no \n at end of csv dataset + empty last column'
2025-03-26 20:24:01 -05:00
marilynel
45ec164faa Merge branch 'master' of https://github.com/marilynel/JSON-java 2025-03-23 10:27:57 -07:00
Sean Leary
d4c5136c21 Merge pull request #961 from effad/master
Option to store null value in JSONObject when parsing a map
2025-03-23 10:21:53 -05:00
Robert Lichtenberger
fd0cca3586 Fix cloning of parser configuration. 2025-03-21 10:12:20 +01:00
Robert Lichtenberger
50a5ce256b Merge branch 'stleary:master' into master 2025-03-21 07:27:12 +01:00
Robert Lichtenberger
1afd7cd6bc Use better name for parser configuration option, fix API comment. 2025-03-21 07:25:37 +01:00
Sean Leary
7751b397bf Merge pull request #960 from marilynel/master
Updated configuration to enable strictMode unit testing with Maven
2025-03-19 11:39:35 -05:00
Robert Lichtenberger
5d1c789490 Add test for JSONArray from Java collection. 2025-03-19 08:10:33 +01:00
Robert Lichtenberger
d1327c2da3 Allow to configure Java null handling. 2025-03-19 07:59:57 +01:00
marilynel
b2943b8fd0 fixed issue #943 Csv parsing skip last row if last line is missing newline 2025-03-16 12:50:58 -07:00
Marilyn Leary
628d8c42d9 Merge branch 'stleary:master' into master 2025-03-16 10:38:04 -07:00
marilynel
76ee4312b3 readme edit 2025-03-16 10:36:24 -07:00
marilynel
4a662316f7 edited pom.xml for mvn testing with strict mode 2025-03-16 10:33:14 -07:00
Sean Leary
6452a6f38d Merge pull request #955 from marilynel/master
Add testWithStrictMode option to build.gradle
2025-03-05 20:30:24 -06:00
marilynel
ae4f4afcc7 dont mess with my line 2025-03-02 11:08:00 -08:00
marilynel
8a86894c63 test with strict mode enabled and fixed 2025-03-02 11:02:27 -08:00
marilynel
f30167e7c0 tests seem to be working, run with strictMode = fale then true 2025-02-23 22:00:22 -08:00
Sean Leary
75e5a3d646 Merge pull request #951 from marilynel/master
Fixing and updating unit tests for default strictMode
2025-02-21 07:37:47 -06:00
marilynel
3919abd69a optimized unit tests to respond accurately to default strictMode 2025-02-15 12:30:12 -08:00
marilynel
f112a091aa fixed failing unit tests in strict mode, issue 940 2025-02-15 12:03:03 -08:00
Sean Leary
42afb34045 Merge pull request #949 from marilynel/master
deprecated unnecessary setter method
2025-02-15 09:59:56 -06:00
Marilyn Leary
a746322e57 Merge branch 'stleary:master' into master 2025-02-09 19:36:46 -08:00
Sean Leary
c524cd17a0 Merge pull request #950 from stleary/upgrade-upload-artifact-in-pipeline
upgrade-upload-artifact-in-pipeline update from v3 to v4
2025-02-09 15:01:26 -06:00
Sean Leary
52f249c71e upgrade-upload-artifact-in-pipeline update from v3 to v4 2025-02-09 14:47:18 -06:00
marilynel
1689fc28cf deprecated unnecessary setter method 2025-02-09 11:13:22 -08:00
Sean Leary
22f8290840 Merge pull request #948 from Simulant87/947-JSONTokener-configuration-ignored
use JSONParserConfiguration of JSONTokener in JSONObject and JSONArray constructor instead of creating a new one
2025-01-19 09:09:42 -06:00
Sean Leary
8b857da467 Merge pull request #946 from Simulant87/928-javadoc-warning-JSONParserConfiguration
#928 add missing javaDoc for JSONParserConfiguration
2025-01-19 09:07:53 -06:00
Sean Leary
07b1291448 Merge pull request #942 from michael-ameri/fix-clone
add missing fields when cloning JSONParserConfiguration
2025-01-19 09:06:21 -06:00
Sean Leary
1d81e8879a Merge pull request #938 from Simulant87/remove-references
Strict mode unit tests
2025-01-19 09:03:47 -06:00
Simulant
4c873a1db4 #947 use JSONParserConfiguration of JSONTokener in JSONObject and JSONArray constructor 2025-01-15 21:41:01 +01:00
Simulant
6631b80e8f #947 add new failing tests with JSONTokener having strict mode configuration 2025-01-15 21:38:46 +01:00
Simulant
9218f28db8 #928 add missing java dock for JSONParserConfiguration
(cherry picked from commit afd9a6fbb7)
2025-01-15 21:02:25 +01:00
Simulant
94341cd663 Revert "#928 add missing java dock for JSONParserConfiguration"
This reverts commit afd9a6fbb7.
2025-01-15 20:58:45 +01:00
Simulant
afd9a6fbb7 #928 add missing java dock for JSONParserConfiguration 2025-01-15 20:55:13 +01:00
Simulant87
54470e6b56 Merge branch 'stleary:master' into remove-references 2025-01-15 20:48:24 +01:00
Sean Leary
dde9d7eceb Merge pull request #931 from stleary/remove-duplicate-moditect
remove-duplicate-moditect: from pom.xml
2025-01-15 07:46:37 -06:00
Sean Leary
8c427d9101 Merge pull request #937 from michael-ameri/remove-references
Update JSONParserConfiguration usage in JSONTokener, JSONArray, and JSONObject
2025-01-15 07:45:50 -06:00
Michael Ameri
4bbbe77446 add missing fields when cloning 2025-01-12 23:03:31 +01:00
Simulant
ad44a9274c add new test cases for JSONObject and JSONArray Constructors with JSONTokener and strict mode 2025-01-11 21:43:04 +01:00
Simulant
3b7ba07531 add test for invalid input on JSONTokener 2025-01-11 21:40:41 +01:00
Simulant
215f4268bf add Javadoc and rename parameters to speaking variable names 2025-01-11 21:35:36 +01:00
Michael Ameri
ca1c6830c9 remove field references to JSONTokener and JSONParserConfiguration in JSONArray
and JSONObject
2025-01-10 18:05:27 +01:00
Sean Leary
2e153737b1 remove-duplicate-moditect: from pom.xml 2025-01-08 07:33:37 -06:00
Sean Leary
391c86931b Merge pull request #930 from stleary/pre-release-20250107
pre-release-20250107
2025-01-07 15:40:58 -06:00
Sean Leary
ed8c73964a pre-release-20250107 2025-01-07 15:30:43 -06:00
Sean Leary
324090a876 Merge pull request #929 from stleary/restore-moditect-pom.xml
restore-moditect-pom.xml restore plugin
2025-01-07 15:24:21 -06:00
Sean Leary
41c6e9e81e restore-moditect-pom.xml restore plugin 2025-01-07 07:47:46 -06:00
Sean Leary
0e60592bdb Merge pull request #924 from stleary/pre-release-20241224
pre-release-20241224: updates for next release
2024-12-24 08:27:15 -06:00
38 changed files with 3053 additions and 868 deletions

View File

@@ -29,7 +29,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -40,4 +40,4 @@ jobs:
- run: "mvn clean compile -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View File

@@ -15,7 +15,7 @@ jobs:
name: Java 1.6
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Setup java
uses: actions/setup-java@v1
with:
@@ -30,7 +30,7 @@ jobs:
jar cvf target/org.json.jar -C target/classes .
- name: Upload JAR 1.6
if: ${{ always() }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v5
with:
name: Create java 1.6 JAR
path: target/*.jar
@@ -45,9 +45,9 @@ jobs:
java: [ 8 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
@@ -64,13 +64,13 @@ jobs:
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@v3
uses: actions/upload-artifact@v5
with:
name: Test Results ${{ matrix.java }}
path: target/surefire-reports/
- name: Upload Test Report ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v5
with:
name: Test Report ${{ matrix.java }}
path: target/site/
@@ -78,7 +78,7 @@ jobs:
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
uses: actions/upload-artifact@v5
with:
name: Package Jar ${{ matrix.java }}
path: target/*.jar
@@ -93,9 +93,9 @@ jobs:
java: [ 11 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
@@ -112,13 +112,13 @@ jobs:
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@v3
uses: actions/upload-artifact@v5
with:
name: Test Results ${{ matrix.java }}
path: target/surefire-reports/
- name: Upload Test Report ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v5
with:
name: Test Report ${{ matrix.java }}
path: target/site/
@@ -126,7 +126,7 @@ jobs:
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
uses: actions/upload-artifact@v5
with:
name: Package Jar ${{ matrix.java }}
path: target/*.jar
@@ -141,9 +141,9 @@ jobs:
java: [ 17 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
@@ -160,13 +160,13 @@ jobs:
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@v3
uses: actions/upload-artifact@v5
with:
name: Test Results ${{ matrix.java }}
path: target/surefire-reports/
- name: Upload Test Report ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v5
with:
name: Test Report ${{ matrix.java }}
path: target/site/
@@ -174,7 +174,7 @@ jobs:
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
uses: actions/upload-artifact@v5
with:
name: Package Jar ${{ matrix.java }}
path: target/*.jar
@@ -189,9 +189,9 @@ jobs:
java: [ 21 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
@@ -208,13 +208,13 @@ jobs:
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@v3
uses: actions/upload-artifact@v5
with:
name: Test Results ${{ matrix.java }}
path: target/surefire-reports/
- name: Upload Test Report ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v5
with:
name: Test Report ${{ matrix.java }}
path: target/site/
@@ -222,7 +222,56 @@ jobs:
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
uses: actions/upload-artifact@v5
with:
name: Package Jar ${{ matrix.java }}
path: target/*.jar
build-25:
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 1
matrix:
# build against supported Java LTS versions:
java: [ 25 ]
name: Java ${{ matrix.java }}
steps:
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Compile Java ${{ matrix.java }}
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 -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 -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@v5
with:
name: Test Results ${{ matrix.java }}
path: target/surefire-reports/
- name: Upload Test Report ${{ matrix.java }}
if: ${{ always() }}
uses: actions/upload-artifact@v5
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@v5
with:
name: Package Jar ${{ matrix.java }}
path: target/*.jar

View File

@@ -9,8 +9,9 @@ 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)
[![javadoc](https://javadoc.io/badge2/org.json/json/javadoc.svg)](https://javadoc.io/doc/org.json/json)
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20241224/json-20241224.jar)**
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20251224/json-20251224.jar)**
# Overview
@@ -26,7 +27,7 @@ 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 - 21
* Designed and tested to use on Java versions 1.6 - 25
The files in this package implement JSON encoders and decoders. The package can also convert between JSON and XML, HTTP headers, Cookies, and CDL.
@@ -97,6 +98,18 @@ Execute the test suite with Gradlew:
gradlew clean build test
```
*Optional* Execute the test suite in strict mode with Gradlew:
```shell
gradlew testWithStrictMode
```
*Optional* Execute the test suite in strict mode with Maven:
```shell
mvn test -P test-strict-mode
```
# Notes
For more information, please see [NOTES.md](https://github.com/stleary/JSON-java/blob/master/docs/NOTES.md)

View File

@@ -3,9 +3,10 @@
*/
apply plugin: 'java'
apply plugin: 'eclipse'
// apply plugin: 'jacoco'
apply plugin: 'jacoco'
apply plugin: 'maven-publish'
// for now, publishing to maven is still a manual process
//plugins {
// id 'java'
//id 'maven-publish'
@@ -19,6 +20,17 @@ repositories {
}
}
// To view the report open build/reports/jacoco/test/html/index.html
jacocoTestReport {
reports {
html.required = true
}
}
test {
finalizedBy jacocoTestReport
}
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.jayway.jsonpath:json-path:2.9.0'
@@ -30,7 +42,7 @@ subprojects {
}
group = 'org.json'
version = 'v20230618-SNAPSHOT'
version = 'v20251224-SNAPSHOT'
description = 'JSON in Java'
sourceCompatibility = '1.8'
@@ -53,3 +65,75 @@ publishing {
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
// Add these imports at the top of your build.gradle file
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
// Your existing build configurations...
// Add a new task to modify the file
task modifyStrictMode {
doLast {
println "Modifying JSONParserConfiguration.java to enable strictMode..."
def filePath = project.file('src/main/java/org/json/JSONParserConfiguration.java')
if (!filePath.exists()) {
throw new GradleException("Could not find file: ${filePath.absolutePath}")
}
// Create a backup of the original file
def backupFile = new File(filePath.absolutePath + '.bak')
Files.copy(filePath.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
// Read and modify the file content
def content = filePath.text
def modifiedContent = content.replace('// this.strictMode = true;', 'this.strictMode = true;')
// Write the modified content back to the file
filePath.text = modifiedContent
println "File modified successfully at: ${filePath.absolutePath}"
}
}
// Add a task to restore the original file
task restoreStrictMode {
doLast {
println "Restoring original JSONParserConfiguration.java..."
def filePath = project.file('src/main/java/org/json/JSONParserConfiguration.java')
def backupFile = new File(filePath.absolutePath + '.bak')
if (backupFile.exists()) {
Files.copy(backupFile.toPath(), filePath.toPath(), StandardCopyOption.REPLACE_EXISTING)
backupFile.delete()
println "Original file restored successfully at: ${filePath.absolutePath}"
} else {
println "Backup file not found at: ${backupFile.absolutePath}. No restoration performed."
}
}
}
// Create a task to run the workflow
task testWithStrictMode {
dependsOn modifyStrictMode
finalizedBy restoreStrictMode
doLast {
// This will trigger a clean build and run tests with strictMode enabled
if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {
exec {
executable 'cmd'
args '/c', 'gradlew.bat', 'clean', 'build'
}
} else {
exec {
executable './gradlew'
args 'clean', 'build'
}
}
}
}

View File

@@ -5,7 +5,14 @@ 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)
~~~
20241224....Strict mode opt-in feature, and recent commits.
20251224 Records, fromJson(), and recent commits
20250517 Strict mode hardening and recent commits
20250107 Restore moditect in pom.xml
20241224 Strict mode opt-in feature, and recent commits. This release does not contain module-info.class.
It is not recommended if you need this feature.
20240303 Revert optLong/getLong changes, and recent commits.

53
pom.xml
View File

@@ -3,7 +3,7 @@
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20241224</version>
<version>20251224</version>
<packaging>bundle</packaging>
<name>JSON in Java</name>
@@ -200,4 +200,55 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>test-strict-mode</id>
<build>
<plugins>
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.3</version>
<executions>
<!-- Enable strict mode -->
<execution>
<id>enable-strict-mode</id>
<phase>process-sources</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<file>src/main/java/org/json/JSONParserConfiguration.java</file>
<replacements>
<replacement>
<token>// this.strictMode = true;</token>
<value>this.strictMode = true;</value>
</replacement>
</replacements>
</configuration>
</execution>
<!-- Restore original code after tests -->
<execution>
<id>restore-original</id>
<phase>test</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<file>src/main/java/org/json/JSONParserConfiguration.java</file>
<replacements>
<replacement>
<token>this.strictMode = true;</token>
<value>// this.strictMode = true;</value>
</replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -27,7 +27,9 @@ public class CDL {
/**
* Constructs a new CDL object.
* @deprecated (Utility class cannot be instantiated)
*/
@Deprecated
public CDL() {
}
@@ -100,11 +102,15 @@ public class CDL {
for (;;) {
String value = getValue(x,delimiter);
char c = x.next();
if (value == null ||
(ja.length() == 0 && value.length() == 0 && c != delimiter)) {
if (value != null) {
ja.put(value);
} else if (ja.length() == 0 && c != delimiter) {
return null;
} else {
// This line accounts for CSV ending with no newline
ja.put("");
}
ja.put(value);
for (;;) {
if (c == delimiter) {
break;
@@ -179,7 +185,7 @@ public class CDL {
Object object = ja.opt(i);
if (object != null) {
String string = object.toString();
if (string.length() > 0 && (string.indexOf(delimiter) >= 0 ||
if (!string.isEmpty() && (string.indexOf(delimiter) >= 0 ||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
sb.append('"');
@@ -307,6 +313,17 @@ public class CDL {
if (ja.length() == 0) {
return null;
}
// The following block accounts for empty datasets (no keys or vals)
if (ja.length() == 1) {
JSONObject j = ja.getJSONObject(0);
if (j.length() == 1) {
String key = j.keys().next();
if ("".equals(key) && "".equals(j.get(key))) {
return null;
}
}
}
return ja;
}

View File

@@ -17,7 +17,9 @@ public class Cookie {
/**
* Constructs a new Cookie object.
* @deprecated (Utility class cannot be instantiated)
*/
@Deprecated()
public Cookie() {
}

View File

@@ -13,7 +13,9 @@ public class CookieList {
/**
* Constructs a new CookieList object.
* @deprecated (Utility class cannot be instantiated)
*/
@Deprecated
public CookieList() {
}

View File

@@ -67,12 +67,6 @@ public class JSONArray implements Iterable<Object> {
*/
private final ArrayList<Object> myArrayList;
// strict mode checks after constructor require access to this object
private JSONTokener jsonTokener;
// strict mode checks after constructor require access to this object
private JSONParserConfiguration jsonParserConfiguration;
/**
* Construct an empty JSONArray.
*/
@@ -89,7 +83,7 @@ public class JSONArray implements Iterable<Object> {
* If there is a syntax error.
*/
public JSONArray(JSONTokener x) throws JSONException {
this(x, new JSONParserConfiguration());
this(x, x.getJsonParserConfiguration());
}
/**
@@ -102,14 +96,7 @@ public class JSONArray implements Iterable<Object> {
public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this();
if (this.jsonParserConfiguration == null) {
this.jsonParserConfiguration = jsonParserConfiguration;
}
if (this.jsonTokener == null) {
this.jsonTokener = x;
this.jsonTokener.setJsonParserConfiguration(this.jsonParserConfiguration);
}
boolean isInitial = x.getPrevious() == 0;
if (x.nextClean() != '[') {
throw x.syntaxError("A JSONArray text must start with '['");
}
@@ -118,6 +105,8 @@ public class JSONArray implements Iterable<Object> {
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
} else if (nextChar==',' && jsonParserConfiguration.isStrictMode()) {
throw x.syntaxError("Array content starts with a ','");
}
if (nextChar != ']') {
x.back();
@@ -129,41 +118,61 @@ public class JSONArray implements Iterable<Object> {
x.back();
this.myArrayList.add(x.nextValue());
}
switch (x.nextClean()) {
case 0:
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
case ',':
nextChar = x.nextClean();
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
}
if (nextChar == ']') {
// trailing commas are not allowed in strict mode
if (jsonParserConfiguration.isStrictMode()) {
throw x.syntaxError("Strict mode error: Expected another array element");
}
return;
}
if (nextChar == ',') {
// consecutive commas are not allowed in strict mode
if (jsonParserConfiguration.isStrictMode()) {
throw x.syntaxError("Strict mode error: Expected a valid array element");
}
return;
}
x.back();
break;
case ']':
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
if (checkForSyntaxError(x, jsonParserConfiguration, isInitial)) return;
}
} else {
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
}
}
}
/** Convenience function. Checks for JSON syntax error.
* @param x A JSONTokener instance from which the JSONArray is constructed.
* @param jsonParserConfiguration A JSONParserConfiguration instance that controls the behavior of the parser.
* @param isInitial Boolean indicating position of char
* @return
*/
private static boolean checkForSyntaxError(JSONTokener x, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) {
char nextChar;
switch (x.nextClean()) {
case 0:
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
case ',':
nextChar = x.nextClean();
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
}
if (nextChar == ']') {
// trailing commas are not allowed in strict mode
if (jsonParserConfiguration.isStrictMode()) {
throw x.syntaxError("Strict mode error: Expected another array element");
}
return true;
}
if (nextChar == ',') {
// consecutive commas are not allowed in strict mode
if (jsonParserConfiguration.isStrictMode()) {
throw x.syntaxError("Strict mode error: Expected a valid array element");
}
return true;
}
x.back();
break;
case ']':
if (isInitial && jsonParserConfiguration.isStrictMode() &&
x.nextClean() != 0) {
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
}
return true;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
return false;
}
/**
* Construct a JSONArray from a source JSON text.
*
@@ -176,11 +185,6 @@ public class JSONArray implements Iterable<Object> {
*/
public JSONArray(String source) throws JSONException {
this(source, new JSONParserConfiguration());
// Strict mode does not allow trailing chars
if (this.jsonParserConfiguration.isStrictMode() &&
this.jsonTokener.nextClean() != 0) {
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
}
}
/**
@@ -195,12 +199,7 @@ public class JSONArray implements Iterable<Object> {
* If there is a syntax error.
*/
public JSONArray(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this(new JSONTokener(source), jsonParserConfiguration);
// Strict mode does not allow trailing chars
if (this.jsonParserConfiguration.isStrictMode() &&
this.jsonTokener.nextClean() != 0) {
throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text");
}
this(new JSONTokener(source, jsonParserConfiguration), jsonParserConfiguration);
}
/**
@@ -349,13 +348,11 @@ public class JSONArray implements Iterable<Object> {
*/
public boolean getBoolean(int index) throws JSONException {
Object object = this.get(index);
if (object.equals(Boolean.FALSE)
|| (object instanceof String && ((String) object)
.equalsIgnoreCase("false"))) {
if (Boolean.FALSE.equals(object)
|| (object instanceof String && "false".equalsIgnoreCase((String) object))) {
return false;
} else if (object.equals(Boolean.TRUE)
|| (object instanceof String && ((String) object)
.equalsIgnoreCase("true"))) {
} else if (Boolean.TRUE.equals(object)
|| (object instanceof String && "true".equalsIgnoreCase((String) object))) {
return true;
}
throw wrongValueFormatException(index, "boolean", object, null);
@@ -750,11 +747,7 @@ public class JSONArray implements Iterable<Object> {
if (val == null) {
return defaultValue;
}
final double doubleValue = val.doubleValue();
// if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
// return defaultValue;
// }
return doubleValue;
return val.doubleValue();
}
/**
@@ -786,11 +779,7 @@ public class JSONArray implements Iterable<Object> {
if (val == null) {
return defaultValue;
}
final Double doubleValue = val.doubleValue();
// if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
// return defaultValue;
// }
return doubleValue;
return val.doubleValue();
}
/**
@@ -822,11 +811,7 @@ public class JSONArray implements Iterable<Object> {
if (val == null) {
return defaultValue;
}
final float floatValue = val.floatValue();
// if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
// return floatValue;
// }
return floatValue;
return val.floatValue();
}
/**
@@ -858,11 +843,7 @@ public class JSONArray implements Iterable<Object> {
if (val == null) {
return defaultValue;
}
final Float floatValue = val.floatValue();
// if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
// return floatValue;
// }
return floatValue;
return val.floatValue();
}
/**
@@ -1660,29 +1641,44 @@ public class JSONArray implements Iterable<Object> {
if(valueThis == null) {
return false;
}
if (valueThis instanceof JSONObject) {
if (!((JSONObject)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof JSONArray) {
if (!((JSONArray)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof Number && valueOther instanceof Number) {
if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) {
return false;
}
} else if (valueThis instanceof JSONString && valueOther instanceof JSONString) {
if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) {
return false;
}
} else if (!valueThis.equals(valueOther)) {
if (!isSimilar(valueThis, valueOther)) {
return false;
}
}
return true;
}
/**
* Convenience function; checks for object similarity
* @param valueThis
* Initial object to compare
* @param valueOther
* Comparison object
* @return boolean
*/
private boolean isSimilar(Object valueThis, Object valueOther) {
if (valueThis instanceof JSONObject) {
if (!((JSONObject)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof JSONArray) {
if (!((JSONArray)valueThis).similar(valueOther)) {
return false;
}
} else if (valueThis instanceof Number && valueOther instanceof Number) {
if (!JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther)) {
return false;
}
} else if (valueThis instanceof JSONString && valueOther instanceof JSONString) {
if (!((JSONString) valueThis).toJSONString().equals(((JSONString) valueOther).toJSONString())) {
return false;
}
} else if (!valueThis.equals(valueOther)) {
return false;
}
return true;
}
/**
* Produce a JSONObject by combining a JSONArray of names with the values of
* this JSONArray.
@@ -1814,12 +1810,7 @@ public class JSONArray implements Iterable<Object> {
writer.write('[');
if (length == 1) {
try {
JSONObject.writeValue(writer, this.myArrayList.get(0),
indentFactor, indent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONArray value at index: 0", e);
}
writeArrayAttempt(writer, indentFactor, indent, 0);
} else if (length != 0) {
final int newIndent = indent + indentFactor;
@@ -1831,12 +1822,7 @@ public class JSONArray implements Iterable<Object> {
writer.write('\n');
}
JSONObject.indent(writer, newIndent);
try {
JSONObject.writeValue(writer, this.myArrayList.get(i),
indentFactor, newIndent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONArray value at index: " + i, e);
}
writeArrayAttempt(writer, indentFactor, newIndent, i);
needsComma = true;
}
if (indentFactor > 0) {
@@ -1851,6 +1837,26 @@ public class JSONArray implements Iterable<Object> {
}
}
/**
* Convenience function. Attempts to write
* @param writer
* Writes the serialized JSON
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @param indent
* The indentation of the top level.
* @param i
* Index in array to be added
*/
private void writeArrayAttempt(Writer writer, int indentFactor, int indent, int i) {
try {
JSONObject.writeValue(writer, this.myArrayList.get(i),
indentFactor, indent);
} catch (Exception e) {
throw new JSONException("Unable to write JSONArray value at index: " + i, e);
}
}
/**
* Returns a java.util.List containing all of the elements in this array.
* If an element in the array is a JSONArray or JSONObject it will also

View File

@@ -16,10 +16,13 @@ public class JSONML {
/**
* Constructs a new JSONML object.
* @deprecated (Utility class cannot be instantiated)
*/
@Deprecated
public JSONML() {
}
/**
* Parse XML values and store them in a JSONArray.
* @param x The XMLTokener containing the source string.
@@ -111,7 +114,7 @@ public class JSONML {
}
} else if (c == '[') {
token = x.nextToken();
if (token.equals("CDATA") && x.next() == '[') {
if ("CDATA".equals(token) && x.next() == '[') {
if (ja != null) {
ja.put(x.nextCDATA());
}
@@ -239,9 +242,21 @@ public class JSONML {
}
} else {
if (ja != null) {
ja.put(token instanceof String
? (config.isKeepStrings() ? XML.unescape((String)token) : XML.stringToValue((String)token))
: token);
Object value;
if (token instanceof String) {
String strToken = (String) token;
if (config.isKeepStrings()) {
value = XML.unescape(strToken);
} else {
value = XML.stringToValue(strToken);
}
} else {
value = token;
}
ja.put(value);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,11 @@ public class JSONParserConfiguration extends ParserConfiguration {
* Used to indicate whether to overwrite duplicate key or not.
*/
private boolean overwriteDuplicateKey;
/**
* Used to indicate whether to convert java null values to JSONObject.NULL or ignoring the entry when converting java maps.
*/
private boolean useNativeNulls;
/**
* Configuration with the default values.
@@ -15,6 +20,8 @@ public class JSONParserConfiguration extends ParserConfiguration {
public JSONParserConfiguration() {
super();
this.overwriteDuplicateKey = false;
// DO NOT DELETE THE FOLLOWING LINE -- it is used for strictMode testing
// this.strictMode = true;
}
/**
@@ -27,7 +34,10 @@ public class JSONParserConfiguration extends ParserConfiguration {
protected JSONParserConfiguration clone() {
JSONParserConfiguration clone = new JSONParserConfiguration();
clone.overwriteDuplicateKey = overwriteDuplicateKey;
clone.strictMode = strictMode;
clone.maxNestingDepth = maxNestingDepth;
clone.keepStrings = keepStrings;
clone.useNativeNulls = useNativeNulls;
return clone;
}
@@ -63,6 +73,21 @@ public class JSONParserConfiguration extends ParserConfiguration {
return clone;
}
/**
* Controls the parser's behavior when meeting Java null values while converting maps.
* If set to true, the parser will put a JSONObject.NULL into the resulting JSONObject.
* Or the map entry will be ignored.
*
* @param useNativeNulls defines if the parser should convert null values in Java maps
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public JSONParserConfiguration withUseNativeNulls(final boolean useNativeNulls) {
JSONParserConfiguration clone = this.clone();
clone.useNativeNulls = useNativeNulls;
return clone;
}
/**
* Sets the strict mode configuration for the JSON parser with default true value
@@ -102,8 +127,23 @@ public class JSONParserConfiguration extends ParserConfiguration {
public boolean isOverwriteDuplicateKey() {
return this.overwriteDuplicateKey;
}
/**
* The parser's behavior when meeting a null value in a java map, controls whether the parser should
* write a JSON entry with a null value (<code>isUseNativeNulls() == true</code>)
* or ignore that map entry (<code>isUseNativeNulls() == false</code>).
*
* @return The <code>useNativeNulls</code> configuration value.
*/
public boolean isUseNativeNulls() {
return this.useNativeNulls;
}
/**
* The parser throws an Exception when strict mode is true and tries to parse invalid JSON characters.
* Otherwise, the parser is more relaxed and might tolerate some invalid characters.
*
* @return the current strict mode setting.
*/
public boolean isStrictMode() {

View File

@@ -127,7 +127,7 @@ public class JSONPointer {
if (pointer == null) {
throw new NullPointerException("pointer cannot be null");
}
if (pointer.isEmpty() || pointer.equals("#")) {
if (pointer.isEmpty() || "#".equals(pointer)) {
this.refTokens = Collections.emptyList();
return;
}
@@ -246,7 +246,7 @@ public class JSONPointer {
*/
@Override
public String toString() {
StringBuilder rval = new StringBuilder("");
StringBuilder rval = new StringBuilder();
for (String token: this.refTokens) {
rval.append('/').append(escape(token));
}

View File

@@ -38,9 +38,21 @@ public class JSONTokener {
/**
* Construct a JSONTokener from a Reader. The caller must close the Reader.
*
* @param reader A reader.
* @param reader the source.
*/
public JSONTokener(Reader reader) {
this(reader, new JSONParserConfiguration());
}
/**
* Construct a JSONTokener from a Reader with a given JSONParserConfiguration. The caller must close the Reader.
*
* @param reader the source.
* @param jsonParserConfiguration A JSONParserConfiguration instance that controls the behavior of the parser.
*
*/
public JSONTokener(Reader reader, JSONParserConfiguration jsonParserConfiguration) {
this.jsonParserConfiguration = jsonParserConfiguration;
this.reader = reader.markSupported()
? reader
: new BufferedReader(reader);
@@ -53,23 +65,40 @@ public class JSONTokener {
this.line = 1;
}
/**
* Construct a JSONTokener from an InputStream. The caller must close the input stream.
* @param inputStream The source.
*/
public JSONTokener(InputStream inputStream) {
this(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
this(inputStream, new JSONParserConfiguration());
}
/**
* Construct a JSONTokener from an InputStream. The caller must close the input stream.
* @param inputStream The source.
* @param jsonParserConfiguration A JSONParserConfiguration instance that controls the behavior of the parser.
*/
public JSONTokener(InputStream inputStream, JSONParserConfiguration jsonParserConfiguration) {
this(new InputStreamReader(inputStream, Charset.forName("UTF-8")), jsonParserConfiguration);
}
/**
* Construct a JSONTokener from a string.
*
* @param s A source string.
* @param source A source string.
*/
public JSONTokener(String s) {
this(new StringReader(s));
public JSONTokener(String source) {
this(new StringReader(source));
}
/**
* Construct a JSONTokener from an InputStream. The caller must close the input stream.
* @param source The source.
* @param jsonParserConfiguration A JSONParserConfiguration instance that controls the behavior of the parser.
*/
public JSONTokener(String source, JSONParserConfiguration jsonParserConfiguration) {
this(new StringReader(source), jsonParserConfiguration);
}
/**
@@ -83,7 +112,10 @@ public class JSONTokener {
/**
* Setter
* @param jsonParserConfiguration new value for jsonParserConfiguration
*
* @deprecated method should not be used
*/
@Deprecated
public void setJsonParserConfiguration(JSONParserConfiguration jsonParserConfiguration) {
this.jsonParserConfiguration = jsonParserConfiguration;
}
@@ -477,13 +509,26 @@ public class JSONTokener {
string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
} else if (jsonParserConfiguration != null &&
jsonParserConfiguration.isStrictMode() && string.endsWith(".")) {
throw this.syntaxError(String.format("Strict mode error: Value '%s' ends with dot", string));
}
Object obj = JSONObject.stringToValue(string);
// Strict mode only allows strings with explicit double quotes
// if obj is a boolean, look at string
if (jsonParserConfiguration != null &&
jsonParserConfiguration.isStrictMode() &&
obj instanceof String) {
throw this.syntaxError(String.format("Strict mode error: Value '%s' is not surrounded by quotes", obj));
jsonParserConfiguration.isStrictMode()) {
if (obj instanceof Boolean && !"true".equals(string) && !"false".equals(string)) {
// Strict mode only allows lowercase true or false
throw this.syntaxError(String.format("Strict mode error: Value '%s' is not lowercase boolean", obj));
}
else if (obj == JSONObject.NULL && !"null".equals(string)) {
// Strint mode only allows lowercase null
throw this.syntaxError(String.format("Strict mode error: Value '%s' is not lowercase null", obj));
}
else if (obj instanceof String) {
// Strict mode only allows strings with explicit double quotes
throw this.syntaxError(String.format("Strict mode error: Value '%s' is not surrounded by quotes", obj));
}
}
return obj;
}

View File

@@ -9,6 +9,7 @@ import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* This provides static methods to convert an XML text into a JSONObject, and to
@@ -80,7 +81,7 @@ public class XML {
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
private int nextIndex = 0;
private int length = string.length();
private final int length = string.length();
@Override
public boolean hasNext() {
@@ -89,6 +90,9 @@ public class XML {
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
int result = string.codePointAt(this.nextIndex);
this.nextIndex += Character.charCount(result);
return result;
@@ -355,10 +359,20 @@ public class XML {
&& TYPE_ATTR.equals(string)) {
xmlXsiTypeConverter = config.getXsiTypeMap().get(token);
} else if (!nilAttributeFound) {
jsonObject.accumulate(string,
config.isKeepStrings()
? ((String) token)
: stringToValue((String) token));
Object obj = stringToValue((String) token);
if (obj instanceof Boolean) {
jsonObject.accumulate(string,
config.isKeepBooleanAsString()
? ((String) token)
: obj);
} else if (obj instanceof Number) {
jsonObject.accumulate(string,
config.isKeepNumberAsString()
? ((String) token)
: obj);
} else {
jsonObject.accumulate(string, stringToValue((String) token));
}
}
token = null;
} else {
@@ -407,8 +421,23 @@ public class XML {
jsonObject.accumulate(config.getcDataTagName(),
stringToValue(string, xmlXsiTypeConverter));
} else {
jsonObject.accumulate(config.getcDataTagName(),
config.isKeepStrings() ? string : stringToValue(string));
Object obj = stringToValue((String) token);
if (obj instanceof Boolean) {
jsonObject.accumulate(config.getcDataTagName(),
config.isKeepBooleanAsString()
? ((String) token)
: obj);
} else if (obj instanceof Number) {
jsonObject.accumulate(config.getcDataTagName(),
config.isKeepNumberAsString()
? ((String) token)
: obj);
} else if (obj == JSONObject.NULL) {
jsonObject.accumulate(config.getcDataTagName(),
config.isKeepStrings() ? ((String) token) : obj);
} else {
jsonObject.accumulate(config.getcDataTagName(), stringToValue((String) token));
}
}
}
@@ -688,6 +717,44 @@ public class XML {
return toJSONObject(reader, XMLParserConfiguration.ORIGINAL);
}
/**
* Convert a well-formed (but not necessarily valid) XML into a
* JSONObject. Some information may be lost in this transformation because
* JSON is a data format and XML is a document format. XML uses elements,
* attributes, and content text, while JSON uses unordered collections of
* name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <pre>{@code
* &lt;[ [ ]]>}</pre>
* are ignored.
*
* All numbers are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document depending
* on how flag is set.
* All booleans are converted as strings, for true, false will not be coerced to
* booleans but will instead be the exact value as seen in the XML document depending
* on how flag is set.
*
* @param reader The XML source reader.
* @param keepNumberAsString If true, then numeric values will not be coerced into
* numeric values and will instead be left as strings
* @param keepBooleanAsString If true, then boolean values will not be coerced into
* * numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(Reader reader, boolean keepNumberAsString, boolean keepBooleanAsString) throws JSONException {
XMLParserConfiguration xmlParserConfiguration = new XMLParserConfiguration();
if(keepNumberAsString) {
xmlParserConfiguration = xmlParserConfiguration.withKeepNumberAsString(keepNumberAsString);
}
if(keepBooleanAsString) {
xmlParserConfiguration = xmlParserConfiguration.withKeepBooleanAsString(keepBooleanAsString);
}
return toJSONObject(reader, xmlParserConfiguration);
}
/**
* Convert a well-formed (but not necessarily valid) XML into a
* JSONObject. Some information may be lost in this transformation because
@@ -746,6 +813,38 @@ public class XML {
return toJSONObject(new StringReader(string), keepStrings);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject. Some information may be lost in this transformation because
* JSON is a data format and XML is a document format. XML uses elements,
* attributes, and content text, while JSON uses unordered collections of
* name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <pre>{@code
* &lt;[ [ ]]>}</pre>
* are ignored.
*
* All numbers are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document depending
* on how flag is set.
* All booleans are converted as strings, for true, false will not be coerced to
* booleans but will instead be the exact value as seen in the XML document depending
* on how flag is set.
*
* @param string
* The source string.
* @param keepNumberAsString If true, then numeric values will not be coerced into
* numeric values and will instead be left as strings
* @param keepBooleanAsString If true, then boolean values will not be coerced into
* numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(String string, boolean keepNumberAsString, boolean keepBooleanAsString) throws JSONException {
return toJSONObject(new StringReader(string), keepNumberAsString, keepBooleanAsString);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject. Some information may be lost in this transformation because

View File

@@ -22,6 +22,16 @@ public class XMLParserConfiguration extends ParserConfiguration {
*/
// public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; // We could override
/**
* Allow user to control how numbers are parsed
*/
private boolean keepNumberAsString;
/**
* Allow user to control how booleans are parsed
*/
private boolean keepBooleanAsString;
/** Original Configuration of the XML Parser. */
public static final XMLParserConfiguration ORIGINAL
= new XMLParserConfiguration();
@@ -142,7 +152,9 @@ public class XMLParserConfiguration extends ParserConfiguration {
*/
@Deprecated
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH);
super(false, DEFAULT_MAXIMUM_NESTING_DEPTH);
this.keepNumberAsString = keepStrings;
this.keepBooleanAsString = keepStrings;
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull;
}
@@ -163,8 +175,10 @@ public class XMLParserConfiguration extends ParserConfiguration {
*/
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList,
final int maxNestingDepth, final boolean closeEmptyTag) {
super(keepStrings, maxNestingDepth);
final int maxNestingDepth, final boolean closeEmptyTag, final boolean keepNumberAsString, final boolean keepBooleanAsString) {
super(false, maxNestingDepth);
this.keepNumberAsString = keepNumberAsString;
this.keepBooleanAsString = keepBooleanAsString;
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull;
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
@@ -189,7 +203,9 @@ public class XMLParserConfiguration extends ParserConfiguration {
this.xsiTypeMap,
this.forceList,
this.maxNestingDepth,
this.closeEmptyTag
this.closeEmptyTag,
this.keepNumberAsString,
this.keepBooleanAsString
);
config.shouldTrimWhiteSpace = this.shouldTrimWhiteSpace;
return config;
@@ -207,7 +223,43 @@ public class XMLParserConfiguration extends ParserConfiguration {
@SuppressWarnings("unchecked")
@Override
public XMLParserConfiguration withKeepStrings(final boolean newVal) {
return super.withKeepStrings(newVal);
XMLParserConfiguration newConfig = this.clone();
newConfig.keepStrings = newVal;
newConfig.keepNumberAsString = newVal;
newConfig.keepBooleanAsString = newVal;
return newConfig;
}
/**
* When parsing the XML into JSON, specifies if numbers should be kept as strings (<code>1</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @param newVal
* new value to use for the <code>keepNumberAsString</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public XMLParserConfiguration withKeepNumberAsString(final boolean newVal) {
XMLParserConfiguration newConfig = this.clone();
newConfig.keepNumberAsString = newVal;
newConfig.keepStrings = newConfig.keepBooleanAsString && newConfig.keepNumberAsString;
return newConfig;
}
/**
* When parsing the XML into JSON, specifies if booleans should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @param newVal
* new value to use for the <code>withKeepBooleanAsString</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
public XMLParserConfiguration withKeepBooleanAsString(final boolean newVal) {
XMLParserConfiguration newConfig = this.clone();
newConfig.keepBooleanAsString = newVal;
newConfig.keepStrings = newConfig.keepBooleanAsString && newConfig.keepNumberAsString;
return newConfig;
}
/**
@@ -221,6 +273,26 @@ public class XMLParserConfiguration extends ParserConfiguration {
return this.cDataTagName;
}
/**
* When parsing the XML into JSONML, specifies if numbers should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string).
*
* @return The <code>keepStrings</code> configuration value.
*/
public boolean isKeepNumberAsString() {
return this.keepNumberAsString;
}
/**
* When parsing the XML into JSONML, specifies if booleans should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string).
*
* @return The <code>keepStrings</code> configuration value.
*/
public boolean isKeepBooleanAsString() {
return this.keepBooleanAsString;
}
/**
* The name of the key in a JSON Object that indicates a CDATA section. Historically this has
* been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA

View File

@@ -168,6 +168,33 @@ public class CDLTest {
}
}
/**
* Csv parsing skip last row if last field of this row is empty #943
*/
@Test
public void csvParsingCatchesLastRow(){
String data = "Field 1,Field 2,Field 3\n" +
"value11,value12,\n" +
"value21,value22,";
JSONArray jsonArray = CDL.toJSONArray(data);
JSONArray expectedJsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("Field 1", "value11");
jsonObject.put("Field 2", "value12");
jsonObject.put("Field 3", "");
expectedJsonArray.put(jsonObject);
jsonObject = new JSONObject();
jsonObject.put("Field 1", "value21");
jsonObject.put("Field 2", "value22");
jsonObject.put("Field 3", "");
expectedJsonArray.put(jsonObject);
Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray);
}
/**
* Assert that there is no error for a single escaped quote within a properly embedded quote.
*/

View File

@@ -0,0 +1,107 @@
package org.json.junit;
import org.json.HTTPTokener;
import org.json.JSONException;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Tests for JSON-Java HTTPTokener.java
*/
public class HTTPTokenerTest {
/**
* Test parsing a simple unquoted token.
*/
@Test
public void parseSimpleToken() {
HTTPTokener tokener = new HTTPTokener("Content-Type");
String token = tokener.nextToken();
assertEquals("Content-Type", token);
}
/**
* Test parsing multiple tokens separated by whitespace.
*/
@Test
public void parseMultipleTokens() {
HTTPTokener tokener = new HTTPTokener("Content-Type application/json");
String token1 = tokener.nextToken();
String token2 = tokener.nextToken();
assertEquals("Content-Type", token1);
assertEquals("application/json", token2);
}
/**
* Test parsing a double-quoted token.
*/
@Test
public void parseDoubleQuotedToken() {
HTTPTokener tokener = new HTTPTokener("\"application/json\"");
String token = tokener.nextToken();
assertEquals("application/json", token);
}
/**
* Test parsing a single-quoted token.
*/
@Test
public void parseSingleQuotedToken() {
HTTPTokener tokener = new HTTPTokener("'application/json'");
String token = tokener.nextToken();
assertEquals("application/json", token);
}
/**
* Test parsing a quoted token that includes spaces and semicolons.
*/
@Test
public void parseQuotedTokenWithSpaces() {
HTTPTokener tokener = new HTTPTokener("\"text/html; charset=UTF-8\"");
String token = tokener.nextToken();
assertEquals("text/html; charset=UTF-8", token);
}
/**
* Test that unterminated quoted strings throw a JSONException.
*/
@Test
public void throwExceptionOnUnterminatedString() {
HTTPTokener tokener = new HTTPTokener("\"incomplete");
JSONException exception = assertThrows(JSONException.class, tokener::nextToken);
assertTrue(exception.getMessage().contains("Unterminated string"));
}
/**
* Test behavior with empty input string.
*/
@Test
public void parseEmptyInput() {
HTTPTokener tokener = new HTTPTokener("");
String token = tokener.nextToken();
assertEquals("", token);
}
/**
* Test behavior with input consisting only of whitespace.
*/
@Test
public void parseWhitespaceOnly() {
HTTPTokener tokener = new HTTPTokener(" \t \n ");
String token = tokener.nextToken();
assertEquals("", token);
}
/**
* Test parsing tokens separated by multiple whitespace characters.
*/
@Test
public void parseTokensWithMultipleWhitespace() {
HTTPTokener tokener = new HTTPTokener("GET /index.html");
String method = tokener.nextToken();
String path = tokener.nextToken();
assertEquals("GET", method);
assertEquals("/index.html", path);
}
}

View File

@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -227,6 +228,19 @@ public class JSONArrayTest {
Util.checkJSONArrayMaps(jaRaw);
Util.checkJSONArrayMaps(jaInt);
}
@Test
public void jsonArrayByListWithNestedNullValue() {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> sub = new HashMap<String, Object>();
sub.put("nullKey", null);
list.add(sub);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withUseNativeNulls(true);
JSONArray jsonArray = new JSONArray(list, parserConfiguration);
JSONObject subObject = jsonArray.getJSONObject(0);
assertTrue(subObject.has("nullKey"));
assertEquals(JSONObject.NULL, subObject.get("nullKey"));
}
/**
* Tests consecutive calls to putAll with array and collection.
@@ -476,13 +490,18 @@ public class JSONArrayTest {
*/
@Test
public void unquotedText() {
String str = "[value1, something!, (parens), foo@bar.com, 23, 23+45]";
List<Object> expected = Arrays.asList("value1", "something!", "(parens)", "foo@bar.com", 23, "23+45");
// Test should fail if default strictMode is true, pass if false
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
if (jsonParserConfiguration.isStrictMode()) {
System.out.println("Skipping JSONArrayTest unquotedText() when strictMode default is true");
} else {
String str = "[value1, something!, (parens), foo@bar.com, 23, 23+45]";
try {
JSONArray jsonArray = new JSONArray(str);
List<Object> expected = Arrays.asList("value1", "something!", "(parens)", "foo@bar.com", 23, "23+45");
assertEquals("Expected to throw exception due to invalid string", true, false);
} catch (JSONException e) { }
} else {
JSONArray jsonArray = new JSONArray(str);
assertEquals(expected, jsonArray.toList());
}
}
@@ -1509,6 +1528,14 @@ public class JSONArrayTest {
new JSONArray(array);
}
@Test
public void testStrictModeJSONTokener_expectException(){
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration().withStrictMode();
JSONTokener tokener = new JSONTokener("[\"value\"]invalidCharacters", jsonParserConfiguration);
assertThrows(JSONException.class, () -> { new JSONArray(tokener); });
}
public static ArrayList<Object> buildNestedArray(int maxDepth) {
if (maxDepth <= 0) {
return new ArrayList<>();

View File

@@ -36,25 +36,31 @@ public class JSONObjectLocaleTest {
MyLocaleBean myLocaleBean = new MyLocaleBean();
/**
* This is just the control case which happens when the locale.ROOT
* lowercasing behavior is the same as the current locale.
*/
Locale.setDefault(new Locale("en"));
JSONObject jsonen = new JSONObject(myLocaleBean);
assertEquals("expected size 2, found: " +jsonen.length(), 2, jsonen.length());
assertEquals("expected jsonen[i] == beanI", "beanI", jsonen.getString("i"));
assertEquals("expected jsonen[id] == beanId", "beanId", jsonen.getString("id"));
// save and restore the current default locale, to avoid any side effects on other executions in the same JVM
Locale defaultLocale = Locale.getDefault();
try {
/**
* This is just the control case which happens when the locale.ROOT
* lowercasing behavior is the same as the current locale.
*/
Locale.setDefault(new Locale("en"));
JSONObject jsonen = new JSONObject(myLocaleBean);
assertEquals("expected size 2, found: " +jsonen.length(), 2, jsonen.length());
assertEquals("expected jsonen[i] == beanI", "beanI", jsonen.getString("i"));
assertEquals("expected jsonen[id] == beanId", "beanId", jsonen.getString("id"));
/**
* Without the JSON-Java change, these keys would be stored internally as
* starting with the letter, 'ı' (dotless i), since the lowercasing of
* the getI and getId keys would be specific to the Turkish locale.
*/
Locale.setDefault(new Locale("tr"));
JSONObject jsontr = new JSONObject(myLocaleBean);
assertEquals("expected size 2, found: " +jsontr.length(), 2, jsontr.length());
assertEquals("expected jsontr[i] == beanI", "beanI", jsontr.getString("i"));
assertEquals("expected jsontr[id] == beanId", "beanId", jsontr.getString("id"));
/**
* Without the JSON-Java change, these keys would be stored internally as
* starting with the letter, 'ı' (dotless i), since the lowercasing of
* the getI and getId keys would be specific to the Turkish locale.
*/
Locale.setDefault(new Locale("tr"));
JSONObject jsontr = new JSONObject(myLocaleBean);
assertEquals("expected size 2, found: " +jsontr.length(), 2, jsontr.length());
assertEquals("expected jsontr[i] == beanI", "beanI", jsontr.getString("i"));
assertEquals("expected jsontr[id] == beanId", "beanId", jsontr.getString("id"));
} finally {
Locale.setDefault(defaultLocale);
}
}
}

View File

@@ -0,0 +1,179 @@
package org.json.junit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.StringReader;
import org.json.JSONObject;
import org.json.junit.data.GenericBeanInt;
import org.json.junit.data.MyEnum;
import org.json.junit.data.MyNumber;
import org.json.junit.data.PersonRecord;
import org.junit.Ignore;
import org.junit.Test;
/**
* Tests for JSONObject support of Java record types.
*
* NOTE: These tests are currently ignored because PersonRecord is not an actual Java record.
* The implementation now correctly detects actual Java records using reflection (Class.isRecord()).
* These tests will need to be enabled and run with Java 17+ where PersonRecord can be converted
* to an actual record type.
*
* This ensures backward compatibility - regular classes with lowercase method names will not
* be treated as records unless they are actual Java record types.
*/
public class JSONObjectRecordTest {
/**
* Tests that JSONObject can be created from a record-style class.
* Record-style classes use accessor methods like name() instead of getName().
*
* NOTE: Ignored until PersonRecord is converted to an actual Java record (requires Java 17+)
*/
@Test
@Ignore("Requires actual Java record type - PersonRecord needs to be a real record (Java 17+)")
public void jsonObjectByRecord() {
PersonRecord person = new PersonRecord("John Doe", 30, true);
JSONObject jsonObject = new JSONObject(person);
assertEquals("Expected 3 keys in the JSONObject", 3, jsonObject.length());
assertEquals("John Doe", jsonObject.get("name"));
assertEquals(30, jsonObject.get("age"));
assertEquals(true, jsonObject.get("active"));
}
/**
* Test that Object methods (toString, hashCode, equals, etc.) are not included
*
* NOTE: Ignored until PersonRecord is converted to an actual Java record (requires Java 17+)
*/
@Test
@Ignore("Requires actual Java record type - PersonRecord needs to be a real record (Java 17+)")
public void recordStyleClassShouldNotIncludeObjectMethods() {
PersonRecord person = new PersonRecord("Jane Doe", 25, false);
JSONObject jsonObject = new JSONObject(person);
// Should NOT include Object methods
assertFalse("Should not include toString", jsonObject.has("toString"));
assertFalse("Should not include hashCode", jsonObject.has("hashCode"));
assertFalse("Should not include equals", jsonObject.has("equals"));
assertFalse("Should not include clone", jsonObject.has("clone"));
assertFalse("Should not include wait", jsonObject.has("wait"));
assertFalse("Should not include notify", jsonObject.has("notify"));
assertFalse("Should not include notifyAll", jsonObject.has("notifyAll"));
// Should only have the 3 record fields
assertEquals("Should only have 3 fields", 3, jsonObject.length());
}
/**
* Test that enum methods are not included when processing an enum
*/
@Test
public void enumsShouldNotIncludeEnumMethods() {
MyEnum myEnum = MyEnum.VAL1;
JSONObject jsonObject = new JSONObject(myEnum);
// Should NOT include enum-specific methods like name(), ordinal(), values(), valueOf()
assertFalse("Should not include name method", jsonObject.has("name"));
assertFalse("Should not include ordinal method", jsonObject.has("ordinal"));
assertFalse("Should not include declaringClass", jsonObject.has("declaringClass"));
// Enums should still work with traditional getters if they have any
// But should not pick up the built-in enum methods
}
/**
* Test that Number subclass methods are not included
*/
@Test
public void numberSubclassesShouldNotIncludeNumberMethods() {
MyNumber myNumber = new MyNumber();
JSONObject jsonObject = new JSONObject(myNumber);
// Should NOT include Number methods like intValue(), longValue(), etc.
assertFalse("Should not include intValue", jsonObject.has("intValue"));
assertFalse("Should not include longValue", jsonObject.has("longValue"));
assertFalse("Should not include doubleValue", jsonObject.has("doubleValue"));
assertFalse("Should not include floatValue", jsonObject.has("floatValue"));
// Should include the actual getter
assertTrue("Should include number", jsonObject.has("number"));
assertEquals("Should have 1 field", 1, jsonObject.length());
}
/**
* Test that generic bean with get() and is() methods works correctly
*/
@Test
public void genericBeanWithGetAndIsMethodsShouldNotBeIncluded() {
GenericBeanInt bean = new GenericBeanInt(42);
JSONObject jsonObject = new JSONObject(bean);
// Should NOT include standalone get() or is() methods
assertFalse("Should not include standalone 'get' method", jsonObject.has("get"));
assertFalse("Should not include standalone 'is' method", jsonObject.has("is"));
// Should include the actual getters
assertTrue("Should include genericValue field", jsonObject.has("genericValue"));
assertTrue("Should include a field", jsonObject.has("a"));
}
/**
* Test that java.* classes don't have their methods picked up
*/
@Test
public void javaLibraryClassesShouldNotIncludeTheirMethods() {
StringReader reader = new StringReader("test");
JSONObject jsonObject = new JSONObject(reader);
// Should NOT include java.io.Reader methods like read(), reset(), etc.
assertFalse("Should not include read method", jsonObject.has("read"));
assertFalse("Should not include reset method", jsonObject.has("reset"));
assertFalse("Should not include ready method", jsonObject.has("ready"));
assertFalse("Should not include skip method", jsonObject.has("skip"));
// Reader should produce empty JSONObject (no valid properties)
assertEquals("Reader should produce empty JSON", 0, jsonObject.length());
}
/**
* Test mixed case - object with both traditional getters and record-style accessors
*
* NOTE: Ignored until PersonRecord is converted to an actual Java record (requires Java 17+)
*/
@Test
@Ignore("Requires actual Java record type - PersonRecord needs to be a real record (Java 17+)")
public void mixedGettersAndRecordStyleAccessors() {
// PersonRecord has record-style accessors: name(), age(), active()
// These should all be included
PersonRecord person = new PersonRecord("Mixed Test", 40, true);
JSONObject jsonObject = new JSONObject(person);
assertEquals("Should have all 3 record-style fields", 3, jsonObject.length());
assertTrue("Should include name", jsonObject.has("name"));
assertTrue("Should include age", jsonObject.has("age"));
assertTrue("Should include active", jsonObject.has("active"));
}
/**
* Test that methods starting with uppercase are not included (not valid record accessors)
*
* NOTE: Ignored until PersonRecord is converted to an actual Java record (requires Java 17+)
*/
@Test
@Ignore("Requires actual Java record type - PersonRecord needs to be a real record (Java 17+)")
public void methodsStartingWithUppercaseShouldNotBeIncluded() {
PersonRecord person = new PersonRecord("Test", 50, false);
JSONObject jsonObject = new JSONObject(person);
// Record-style accessors must start with lowercase
// Methods like Name(), Age() (uppercase) should not be picked up
// Our PersonRecord only has lowercase accessors, which is correct
assertEquals("Should only have lowercase accessors", 3, jsonObject.length());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONParserConfiguration;
import org.json.JSONTokener;
import org.junit.Test;
import java.io.IOException;
@@ -14,7 +15,10 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class JSONParserConfigurationTest {
private static final String TEST_SOURCE = "{\"key\": \"value1\", \"key\": \"value2\"}";
@@ -32,6 +36,32 @@ public class JSONParserConfigurationTest {
assertEquals("duplicate key should be overwritten", "value2", jsonObject.getString("key"));
}
@Test
public void strictModeIsCloned(){
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
.withStrictMode(true)
.withMaxNestingDepth(12);
assertTrue(jsonParserConfiguration.isStrictMode());
}
@Test
public void maxNestingDepthIsCloned(){
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
.<JSONParserConfiguration>withKeepStrings(true)
.withStrictMode(true);
assertTrue(jsonParserConfiguration.isKeepStrings());
}
@Test
public void useNativeNullsIsCloned() {
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
.withUseNativeNulls(true)
.withStrictMode(true);
assertTrue(jsonParserConfiguration.isUseNativeNulls());
}
@Test
public void verifyDuplicateKeyThenMaxDepth() {
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration()
@@ -490,6 +520,40 @@ public class JSONParserConfigurationTest {
je.getMessage());
}
@Test
public void givenInvalidInputObject_testStrictModeTrue_JSONObjectUsingJSONTokener_shouldThrowJSONException() {
JSONException exception = assertThrows(JSONException.class, () -> {
new JSONObject(new JSONTokener("{\"key\":\"value\"} invalid trailing text"), new JSONParserConfiguration().withStrictMode(true));
});
assertEquals("Strict mode error: Unparsed characters found at end of input text at 17 [character 18 line 1]", exception.getMessage());
}
@Test
public void givenInvalidInputObject_testStrictModeTrue_JSONObjectUsingString_shouldThrowJSONException() {
JSONException exception = assertThrows(JSONException.class, () -> {
new JSONObject("{\"key\":\"value\"} invalid trailing text", new JSONParserConfiguration().withStrictMode(true));
});
assertEquals("Strict mode error: Unparsed characters found at end of input text at 17 [character 18 line 1]", exception.getMessage());
}
@Test
public void givenInvalidInputObject_testStrictModeTrue_JSONArrayUsingJSONTokener_shouldThrowJSONException() {
JSONException exception = assertThrows(JSONException.class, () -> {
new JSONArray(new JSONTokener("[\"value\"] invalid trailing text"), new JSONParserConfiguration().withStrictMode(true));
});
assertEquals("Strict mode error: Unparsed characters found at end of input text at 11 [character 12 line 1]", exception.getMessage());
}
@Test
public void givenInvalidInputObject_testStrictModeTrue_JSONArrayUsingString_shouldThrowJSONException() {
JSONException exception = assertThrows(JSONException.class, () -> {
new JSONArray("[\"value\"] invalid trailing text", new JSONParserConfiguration().withStrictMode(true));
});
assertEquals("Strict mode error: Unparsed characters found at end of input text at 11 [character 12 line 1]", exception.getMessage());
}
/**
* This method contains short but focused use-case samples and is exclusively used to test strictMode unit tests in
* this class.

View File

@@ -16,10 +16,7 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.json.*;
import org.junit.Test;
/**
@@ -98,7 +95,17 @@ public class JSONTokenerTest {
checkValid(" [] ",JSONArray.class);
checkValid("[1,2]",JSONArray.class);
checkValid("\n\n[1,2]\n\n",JSONArray.class);
checkValid("1 2", String.class);
// Test should fail if default strictMode is true, pass if false
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
if (jsonParserConfiguration.isStrictMode()) {
try {
checkValid("1 2", String.class);
assertEquals("Expected to throw exception due to invalid string", true, false);
} catch (JSONException e) { }
} else {
checkValid("1 2", String.class);
}
}
@Test
@@ -325,4 +332,42 @@ public class JSONTokenerTest {
assertEquals("Stream closed", exception.getMessage());
}
}
@Test
public void testInvalidInput_JSONObject_withoutStrictModel_shouldParseInput() {
String input = "{\"invalidInput\": [],}";
JSONTokener tokener = new JSONTokener(input);
// Test should fail if default strictMode is true, pass if false
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
if (jsonParserConfiguration.isStrictMode()) {
try {
Object value = tokener.nextValue();
assertEquals(new JSONObject(input).toString(), value.toString());
assertEquals("Expected to throw exception due to invalid string", true, false);
} catch (JSONException e) { }
} else {
Object value = tokener.nextValue();
assertEquals(new JSONObject(input).toString(), value.toString());
}
}
@Test
public void testInvalidInput_JSONArray_withoutStrictModel_shouldParseInput() {
String input = "[\"invalidInput\",]";
JSONTokener tokener = new JSONTokener(input);
// Test should fail if default strictMode is true, pass if false
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
if (jsonParserConfiguration.isStrictMode()) {
try {
Object value = tokener.nextValue();
assertEquals(new JSONArray(input).toString(), value.toString());
assertEquals("Expected to throw exception due to invalid string", true, false);
} catch (JSONException e) { }
} else {
Object value = tokener.nextValue();
assertEquals(new JSONArray(input).toString(), value.toString());
}
}
}

View File

@@ -574,15 +574,18 @@ public class XMLConfigurationTest {
XMLParserConfiguration keepStringsAndCloseEmptyTag = keepStrings.withCloseEmptyTag(true);
XMLParserConfiguration keepDigits = keepStringsAndCloseEmptyTag.withKeepStrings(false);
XMLParserConfiguration keepDigitsAndNoCloseEmptyTag = keepDigits.withCloseEmptyTag(false);
assertTrue(keepStrings.isKeepStrings());
assertTrue(keepStrings.isKeepNumberAsString());
assertTrue(keepStrings.isKeepBooleanAsString());
assertFalse(keepStrings.isCloseEmptyTag());
assertTrue(keepStringsAndCloseEmptyTag.isKeepStrings());
assertTrue(keepStringsAndCloseEmptyTag.isKeepNumberAsString());
assertTrue(keepStringsAndCloseEmptyTag.isKeepBooleanAsString());
assertTrue(keepStringsAndCloseEmptyTag.isCloseEmptyTag());
assertFalse(keepDigits.isKeepStrings());
assertFalse(keepDigits.isKeepNumberAsString());
assertFalse(keepDigits.isKeepBooleanAsString());
assertTrue(keepDigits.isCloseEmptyTag());
assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepStrings());
assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepNumberAsString());
assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepBooleanAsString());
assertFalse(keepDigitsAndNoCloseEmptyTag.isCloseEmptyTag());
}
/**
@@ -767,6 +770,67 @@ public class XMLConfigurationTest {
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expected);
}
/**
* JSON string lost leading zero and converted "True" to true.
*/
@Test
public void testToJSONArray_jsonOutput_withKeepNumberAsString() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><id>null</id><item id=\"01\"/><title>True</title></root>";
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",\"1\",\"00\",\"0\",null],\"title\":true}}");
final JSONObject actualJsonOutput = XML.toJSONObject(originalXml,
new XMLParserConfiguration().withKeepNumberAsString(true));
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expected);
}
/**
* JSON string lost leading zero and converted "True" to true.
*/
@Test
public void testToJSONArray_jsonOutput_withKeepBooleanAsString() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><id>null</id><item id=\"01\"/><title>True</title></root>";
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",1,\"00\",0,null],\"title\":\"True\"}}");
final JSONObject actualJsonOutput = XML.toJSONObject(originalXml,
new XMLParserConfiguration().withKeepBooleanAsString(true));
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expected);
}
/**
* null is "null" when keepStrings == true
*/
@Test
public void testToJSONArray_jsonOutput_null_withKeepString() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>null</title></root>";
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",\"1\",\"00\",\"0\"],\"title\":\"null\"}}");
final JSONObject actualJsonOutput = XML.toJSONObject(originalXml,
new XMLParserConfiguration().withKeepStrings(true));
Util.compareActualVsExpectedJsonObjects(actualJsonOutput,expected);
}
/**
* Test keepStrings behavior when setting keepBooleanAsString, keepNumberAsString
*/
@Test
public void test_keepStringBehavior() {
XMLParserConfiguration xpc = new XMLParserConfiguration().withKeepStrings(true);
assertEquals(xpc.isKeepStrings(), true);
xpc = xpc.withKeepBooleanAsString(true);
xpc = xpc.withKeepNumberAsString(false);
assertEquals(xpc.isKeepStrings(), false);
xpc = xpc.withKeepBooleanAsString(false);
xpc = xpc.withKeepNumberAsString(true);
assertEquals(xpc.isKeepStrings(), false);
xpc = xpc.withKeepBooleanAsString(true);
xpc = xpc.withKeepNumberAsString(true);
assertEquals(xpc.isKeepStrings(), true);
xpc = xpc.withKeepBooleanAsString(false);
xpc = xpc.withKeepNumberAsString(false);
assertEquals(xpc.isKeepStrings(), false);
}
/**
* JSON string cannot be reverted to original xml.
*/

View File

@@ -0,0 +1,81 @@
package org.json.junit;
import org.json.XMLTokener;
import org.junit.Test;
import java.io.StringReader;
import static org.junit.Assert.*;
/**
* Tests for JSON-Java XMLTokener.java
*/
public class XMLTokenerTest {
/**
* Tests that nextCDATA() correctly extracts content from within a CDATA section.
*/
@Test
public void testNextCDATA() {
String xml = "This is <![CDATA[ some <CDATA> content ]]> after";
XMLTokener tokener = new XMLTokener(new StringReader(xml));
tokener.skipPast("<![CDATA[");
String cdata = tokener.nextCDATA();
assertEquals(" some <CDATA> content ", cdata);
}
/**
* Tests that nextContent() returns plain text content before a tag.
*/
@Test
public void testNextContentWithText() {
String xml = "Some content<nextTag>";
XMLTokener tokener = new XMLTokener(xml);
Object content = tokener.nextContent();
assertEquals("Some content", content);
}
/**
* Tests that nextContent() returns '<' character when starting with a tag.
*/
@Test
public void testNextContentWithTag() {
String xml = "<tag>";
XMLTokener tokener = new XMLTokener(xml);
Object content = tokener.nextContent();
assertEquals('<', content);
}
/**
* Tests that nextEntity() resolves a known entity like &amp; correctly.
*/
@Test
public void testNextEntityKnown() {
XMLTokener tokener = new XMLTokener("amp;");
Object result = tokener.nextEntity('&');
assertEquals("&", result);
}
/**
* Tests that nextEntity() preserves unknown entities by returning them unchanged.
*/
@Test
public void testNextEntityUnknown() {
XMLTokener tokener = new XMLTokener("unknown;");
tokener.next(); // skip 'u'
Object result = tokener.nextEntity('&');
assertEquals("&nknown;", result); // malformed start to simulate unknown
}
/**
* Tests skipPast() to ensure the cursor moves past the specified string.
*/
@Test
public void testSkipPast() {
String xml = "Ignore this... endHere more text";
XMLTokener tokener = new XMLTokener(xml);
tokener.skipPast("endHere");
assertEquals(' ', tokener.next()); // should be the space after "endHere"
}
}

View File

@@ -0,0 +1,23 @@
package org.json.junit.data;
public class CustomClass {
public int number;
public String name;
public Long longNumber;
public CustomClass() {}
public CustomClass (int number, String name, Long longNumber) {
this.number = number;
this.name = name;
this.longNumber = longNumber;
}
@Override
public boolean equals(Object o) {
CustomClass customClass = (CustomClass) o;
return (this.number == customClass.number
&& this.name.equals(customClass.name)
&& this.longNumber.equals(customClass.longNumber));
}
}

View File

@@ -0,0 +1,19 @@
package org.json.junit.data;
import java.math.BigInteger;
public class CustomClassA {
public BigInteger largeInt;
public CustomClassA() {}
public CustomClassA(BigInteger largeInt) {
this.largeInt = largeInt;
}
@Override
public boolean equals(Object o) {
CustomClassA classA = (CustomClassA) o;
return this.largeInt.equals(classA.largeInt);
}
}

View File

@@ -0,0 +1,20 @@
package org.json.junit.data;
public class CustomClassB {
public int number;
public CustomClassC classC;
public CustomClassB() {}
public CustomClassB(int number, CustomClassC classC) {
this.number = number;
this.classC = classC;
}
@Override
public boolean equals(Object o) {
CustomClassB classB = (CustomClassB) o;
return this.number == classB.number
&& this.classC.equals(classB.classC);
}
}

View File

@@ -0,0 +1,34 @@
package org.json.junit.data;
import org.json.JSONObject;
public class CustomClassC {
public String stringName;
public Long longNumber;
public CustomClassC() {}
public CustomClassC(String stringName, Long longNumber) {
this.stringName = stringName;
this.longNumber = longNumber;
}
public JSONObject toJSON() {
JSONObject object = new JSONObject();
object.put("stringName", this.stringName);
object.put("longNumber", this.longNumber);
return object;
}
@Override
public boolean equals(Object o) {
CustomClassC classC = (CustomClassC) o;
return this.stringName.equals(classC.stringName)
&& this.longNumber.equals(classC.longNumber);
}
@Override
public int hashCode() {
return java.util.Objects.hash(stringName, longNumber);
}
}

View File

@@ -0,0 +1,19 @@
package org.json.junit.data;
import java.util.List;
public class CustomClassD {
public List<String> stringList;
public CustomClassD() {}
public CustomClassD(List<String> stringList) {
this.stringList = stringList;
}
@Override
public boolean equals(Object o) {
CustomClassD classD = (CustomClassD) o;
return this.stringList.equals(classD.stringList);
}
}

View File

@@ -0,0 +1,18 @@
package org.json.junit.data;
import java.util.List;
public class CustomClassE {
public List<CustomClassC> listClassC;
public CustomClassE() {}
public CustomClassE(List<CustomClassC> listClassC) {
this.listClassC = listClassC;
}
@Override
public boolean equals(Object o) {
CustomClassE classE = (CustomClassE) o;
return this.listClassC.equals(classE.listClassC);
}
}

View File

@@ -0,0 +1,19 @@
package org.json.junit.data;
import java.util.List;
public class CustomClassF {
public List<List<String>> listOfString;
public CustomClassF() {}
public CustomClassF(List<List<String>> listOfString) {
this.listOfString = listOfString;
}
@Override
public boolean equals(Object o) {
CustomClassF classF = (CustomClassF) o;
return this.listOfString.equals(classF.listOfString);
}
}

View File

@@ -0,0 +1,18 @@
package org.json.junit.data;
import java.util.Map;
public class CustomClassG {
public Map<String, String> dataList;
public CustomClassG () {}
public CustomClassG (Map<String, String> dataList) {
this.dataList = dataList;
}
@Override
public boolean equals(Object object) {
CustomClassG classG = (CustomClassG) object;
return this.dataList.equals(classG.dataList);
}
}

View File

@@ -0,0 +1,22 @@
package org.json.junit.data;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
public class CustomClassH {
public Map<String, List<Integer>> integerMap;
public CustomClassH() {}
public CustomClassH(Map<String, List<Integer>> integerMap) {
this.integerMap = integerMap;
}
@Override
public boolean equals(Object object) {
CustomClassH classH = (CustomClassH) object;
return this.integerMap.size() == classH.integerMap.size()
&& this.integerMap.keySet().equals(classH.integerMap.keySet())
&& new ArrayList<>(this.integerMap.values()).equals(new ArrayList<>(classH.integerMap.values()));
}
}

View File

@@ -0,0 +1,12 @@
package org.json.junit.data;
import java.util.Map;
public class CustomClassI {
public Map<String, Map<String, Integer>> integerMap;
public CustomClassI() {}
public CustomClassI(Map<String, Map<String, Integer>> integerMap) {
this.integerMap = integerMap;
}
}

View File

@@ -0,0 +1,31 @@
package org.json.junit.data;
/**
* A test class that mimics Java record accessor patterns.
* Records use accessor methods without get/is prefixes (e.g., name() instead of getName()).
* This class simulates that behavior to test JSONObject's handling of such methods.
*/
public class PersonRecord {
private final String name;
private final int age;
private final boolean active;
public PersonRecord(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// Record-style accessors (no "get" or "is" prefix)
public String name() {
return name;
}
public int age() {
return age;
}
public boolean active() {
return active;
}
}