Compare commits

89 Commits

Author SHA1 Message Date
Sean Leary
24bba97c1d pre-release-20251224 update docs and builds for next release 2025-12-24 09:05:18 -06: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
31 changed files with 1876 additions and 422 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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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@v4
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

@@ -10,7 +10,7 @@ JSON in Java [package org.json]
[![Java CI with Maven](https://github.com/stleary/JSON-java/actions/workflows/pipeline.yml/badge.svg)](https://github.com/stleary/JSON-java/actions/workflows/pipeline.yml)
[![CodeQL](https://github.com/stleary/JSON-java/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/stleary/JSON-java/actions/workflows/codeql-analysis.yml)
**[Click here if you just want the latest release jar file.](https://search.maven.org/remotecontent?filepath=org/json/json/20250517/json-20250517.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 +26,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.

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 = 'v20250517-SNAPSHOT'
version = 'v20251224-SNAPSHOT'
description = 'JSON in Java'
sourceCompatibility = '1.8'

View File

@@ -5,6 +5,8 @@ and artifactId "json". For example:
[https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav)
~~~
20251224 Records, fromJson(), and recent commits
20250517 Strict mode hardening and recent commits
20250107 Restore moditect in pom.xml

View File

@@ -3,7 +3,7 @@
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250517</version>
<version>20251224</version>
<packaging>bundle</packaging>
<name>JSON in Java</name>

View File

@@ -27,7 +27,9 @@ public class CDL {
/**
* Constructs a new CDL object.
* @deprecated (Utility class cannot be instantiated)
*/
@Deprecated
public CDL() {
}
@@ -183,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('"');

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

@@ -105,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();
@@ -116,41 +118,7 @@ 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 ']':
if (isInitial && jsonParserConfiguration.isStrictMode() &&
x.nextClean() != 0) {
throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text");
}
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
if (checkForSyntaxError(x, jsonParserConfiguration, isInitial)) return;
}
} else {
if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != 0) {
@@ -159,6 +127,52 @@ public class JSONArray implements Iterable<Object> {
}
}
/** 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.
*
@@ -334,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);
@@ -735,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();
}
/**
@@ -771,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();
}
/**
@@ -807,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();
}
/**
@@ -843,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();
}
/**
@@ -1645,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.
@@ -1799,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;
@@ -1816,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) {
@@ -1836,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

@@ -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

@@ -509,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

@@ -428,6 +428,9 @@ public class XML {
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));
}

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

@@ -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());
}
}

View File

@@ -56,6 +56,17 @@ import org.json.junit.data.RecursiveBeanEquals;
import org.json.junit.data.Singleton;
import org.json.junit.data.SingletonEnum;
import org.json.junit.data.WeirdList;
import org.json.junit.data.CustomClass;
import org.json.junit.data.CustomClassA;
import org.json.junit.data.CustomClassB;
import org.json.junit.data.CustomClassC;
import org.json.junit.data.CustomClassD;
import org.json.junit.data.CustomClassE;
import org.json.junit.data.CustomClassF;
import org.json.junit.data.CustomClassG;
import org.json.junit.data.CustomClassH;
import org.json.junit.data.CustomClassI;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
@@ -3895,9 +3906,10 @@ public class JSONObjectTest {
}
@Test
public void issue743SerializationMapWith1000Objects() {
HashMap<String, Object> map = buildNestedMap(1000);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
public void issue743SerializationMapWith500Objects() {
// TODO: find out why 1000 objects no longer works
HashMap<String, Object> map = buildNestedMap(500);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(500);
JSONObject object = new JSONObject(map, parserConfiguration);
String jsonString = object.toString();
}
@@ -3997,6 +4009,56 @@ public class JSONObjectTest {
assertThrows(JSONException.class, () -> { new JSONObject(tokener); });
}
@Test
public void test_strictModeWithMisCasedBooleanOrNullValue(){
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration().withStrictMode();
try{
new JSONObject("{\"a\":True}", jsonParserConfiguration);
fail("Expected an exception");
} catch (JSONException e) {
// No action, expected outcome
}
try{
new JSONObject("{\"a\":TRUE}", jsonParserConfiguration);
fail("Expected an exception");
} catch (JSONException e) {
// No action, expected outcome
}
try{
new JSONObject("{\"a\":nUlL}", jsonParserConfiguration);
fail("Expected an exception");
} catch (JSONException e) {
// No action, expected outcome
}
}
@Test
public void test_strictModeWithInappropriateKey(){
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration().withStrictMode();
// Parsing the following objects should fail
try{
new JSONObject("{true : 3}", jsonParserConfiguration);
fail("Expected an exception");
} catch (JSONException e) {
// No action, expected outcome
}
try{
new JSONObject("{TRUE : 3}", jsonParserConfiguration);
fail("Expected an exception");
} catch (JSONException e) {
// No action, expected outcome
}
try{
new JSONObject("{1 : 3}", jsonParserConfiguration);
fail("Expected an exception");
} catch (JSONException e) {
// No action, expected outcome
}
}
/**
* Method to build nested map of max maxDepth
*
@@ -4011,5 +4073,161 @@ public class JSONObjectTest {
nestedMap.put("t", buildNestedMap(maxDepth - 1));
return nestedMap;
}
/**
* Tests the behavior of the {@link JSONObject} when parsing a bean with null fields
* using a custom {@link JSONParserConfiguration} that enables the use of native nulls.
*
* <p>This test ensures that uninitialized fields in the bean are serialized correctly
* into the resulting JSON object, and their keys are present in the JSON string output.</p>
*/
@Test
public void jsonObjectParseNullFieldsWithParserConfiguration() {
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
RecursiveBean bean = new RecursiveBean(null);
JSONObject jsonObject = new JSONObject(bean, jsonParserConfiguration.withUseNativeNulls(true));
assertTrue("name key should be present", jsonObject.has("name"));
assertTrue("ref key should be present", jsonObject.has("ref"));
assertTrue("ref2 key should be present", jsonObject.has("ref2"));
}
/**
* Tests the behavior of the {@link JSONObject} when parsing a bean with null fields
* without using a custom {@link JSONParserConfiguration}.
*
* <p>This test ensures that uninitialized fields in the bean are not serialized
* into the resulting JSON object, and the object remains empty.</p>
*/
@Test
public void jsonObjectParseNullFieldsWithoutParserConfiguration() {
RecursiveBean bean = new RecursiveBean(null);
JSONObject jsonObject = new JSONObject(bean);
assertTrue("JSONObject should be empty", jsonObject.isEmpty());
}
@Test
public void jsonObjectParseFromJson_0() {
JSONObject object = new JSONObject();
object.put("number", 12);
object.put("name", "Alex");
object.put("longNumber", 1500000000L);
CustomClass customClass = object.fromJson(CustomClass.class);
CustomClass compareClass = new CustomClass(12, "Alex", 1500000000L);
assertEquals(customClass, compareClass);
}
@Test
public void jsonObjectParseFromJson_1() {
JSONObject object = new JSONObject();
BigInteger largeInt = new BigInteger("123");
object.put("largeInt", largeInt.toString());
CustomClassA customClassA = object.fromJson(CustomClassA.class);
CustomClassA compareClassClassA = new CustomClassA(largeInt);
assertEquals(customClassA, compareClassClassA);
}
@Test
public void jsonObjectParseFromJson_2() {
JSONObject object = new JSONObject();
object.put("number", 12);
JSONObject classC = new JSONObject();
classC.put("stringName", "Alex");
classC.put("longNumber", 123456L);
object.put("classC", classC);
CustomClassB customClassB = object.fromJson(CustomClassB.class);
CustomClassC classCObject = new CustomClassC("Alex", 123456L);
CustomClassB compareClassB = new CustomClassB(12, classCObject);
assertEquals(customClassB, compareClassB);
}
@Test
public void jsonObjectParseFromJson_3() {
JSONObject object = new JSONObject();
JSONArray array = new JSONArray();
array.put("test1");
array.put("test2");
array.put("test3");
object.put("stringList", array);
CustomClassD customClassD = object.fromJson(CustomClassD.class);
CustomClassD compareClassD = new CustomClassD(Arrays.asList("test1", "test2", "test3"));
assertEquals(customClassD, compareClassD);
}
@Test
public void jsonObjectParseFromJson_4() {
JSONObject object = new JSONObject();
JSONArray array = new JSONArray();
array.put(new CustomClassC("test1", 1L).toJSON());
array.put(new CustomClassC("test2", 2L).toJSON());
object.put("listClassC", array);
CustomClassE customClassE = object.fromJson(CustomClassE.class);
CustomClassE compareClassE = new CustomClassE(java.util.Arrays.asList(
new CustomClassC("test1", 1L),
new CustomClassC("test2", 2L)));
assertEquals(customClassE, compareClassE);
}
@Test
public void jsonObjectParseFromJson_5() {
JSONObject object = new JSONObject();
JSONArray array = new JSONArray();
array.put(Arrays.asList("A", "B", "C"));
array.put(Arrays.asList("D", "E"));
object.put("listOfString", array);
CustomClassF customClassF = object.fromJson(CustomClassF.class);
List<List<String>> listOfString = new ArrayList<>();
listOfString.add(Arrays.asList("A", "B", "C"));
listOfString.add(Arrays.asList("D", "E"));
CustomClassF compareClassF = new CustomClassF(listOfString);
assertEquals(customClassF, compareClassF);
}
@Test
public void jsonObjectParseFromJson_6() {
JSONObject object = new JSONObject();
Map<String, String> dataList = new HashMap<>();
dataList.put("A", "Aa");
dataList.put("B", "Bb");
dataList.put("C", "Cc");
object.put("dataList", dataList);
CustomClassG customClassG = object.fromJson(CustomClassG.class);
CustomClassG compareClassG = new CustomClassG(dataList);
assertEquals(customClassG, compareClassG);
}
@Test
public void jsonObjectParseFromJson_7() {
JSONObject object = new JSONObject();
Map<String, List<Integer>> dataList = new HashMap<>();
dataList.put("1", Arrays.asList(1, 2, 3, 4));
dataList.put("2", Arrays.asList(2, 3, 4, 5));
object.put("integerMap", dataList);
CustomClassH customClassH = object.fromJson(CustomClassH.class);
CustomClassH compareClassH = new CustomClassH(dataList);
assertEquals(customClassH.integerMap.toString(), compareClassH.integerMap.toString());
}
@Test
public void jsonObjectParseFromJson_8() {
JSONObject object = new JSONObject();
Map<String, Map<String, Integer>> dataList = new HashMap<>();
dataList.put("1", Collections.singletonMap("1", 1));
dataList.put("2", Collections.singletonMap("2", 2));
object.put("integerMap", dataList);
CustomClassI customClassI = object.fromJson(CustomClassI.class);
CustomClassI compareClassI = new CustomClassI(dataList);
assertEquals(customClassI.integerMap.toString(), compareClassI.integerMap.toString());
}
}

View File

@@ -775,8 +775,8 @@ public class XMLConfigurationTest {
*/
@Test
public void testToJSONArray_jsonOutput_withKeepNumberAsString() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",\"1\",\"00\",\"0\"],\"title\":true}}");
final 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);
@@ -787,13 +787,25 @@ public class XMLConfigurationTest {
*/
@Test
public void testToJSONArray_jsonOutput_withKeepBooleanAsString() {
final String originalXml = "<root><id>01</id><id>1</id><id>00</id><id>0</id><item id=\"01\"/><title>True</title></root>";
final JSONObject expected = new JSONObject("{\"root\":{\"item\":{\"id\":\"01\"},\"id\":[\"01\",1,\"00\",0],\"title\":\"True\"}}");
final 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
*/

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;
}
}