Compare commits

...

227 Commits

Author SHA1 Message Date
Sean Leary
3d69990ab5 20240303-pre-release-updates updates for release 2024-03-03 08:47:53 -06:00
Sean Leary
ba05e1a98c Merge pull request #874 from stleary/pipeline-updates
Deployment and Pipeline action updates
2024-03-03 08:02:03 -06:00
Sean Leary
390d442054 pipeline-updates - remove deployment.yml for now, will restore after setting up secrets 2024-03-02 10:00:13 -06:00
Sean Leary
3eb8a62af6 pipeline-updates - space after # char? 2024-03-02 09:57:40 -06:00
Sean Leary
989cdb61bc pipeline-updates - do not build in parallel 2024-03-02 09:15:32 -06:00
Sean Leary
8de0628bd1 pipeline-updates - disable deployment.yml workflow for now (it's not set up in secrets yet) 2024-03-02 08:55:24 -06:00
Sean Leary
569be9d19e Merge branch 'master' into pipeline-updates 2024-03-02 08:34:10 -06:00
Sean Leary
3f97826462 Merge pull request #869 from stleary/revert-recent-objLong-getLong-changes
Revert recent obj long get long changes
2024-02-28 09:52:04 -06:00
Sean Leary
d520210ea2 Added one more example to XMLTest clarifyCurrentBehavior() 2024-02-25 10:45:34 -06:00
Sean Leary
898288810f add unit tests to clarify current behavior for JSONObject and XML 2024-02-24 21:07:12 -06:00
Sean Leary
771c82c4eb backing out recent changes to optLong, getLong. See #868 2024-02-24 13:07:51 -06:00
Sean Leary
d36066cf82 Merge pull request #860 from jscrdev/fixed-javadocs2
Added missing Javadocs for Java 21
2024-02-22 20:18:34 -06:00
Sean Leary
c1107fa987 pipeline-updates: Java 11 intermittent fail - try separate build 2024-02-18 16:17:41 -06:00
Sean Leary
cd631d970e pipeline-updates: Java 11 intermittent fail - try an earlier release (there is no later release 2024-02-18 15:54:29 -06:00
Sean Leary
f0289413d6 pipeline-updates: Java 11 intermittent fail - try increasing stack size 2024-02-18 15:45:13 -06:00
Sean Leary
b4b39bb441 pipeline-updates: Java 11 intermittent test failures, try not running in parallel 2024-02-18 15:29:44 -06:00
Valentyn Kolesnikov
86253211c2 Added missing Javadocs for Java 21 2024-02-18 04:20:33 +02:00
Sean Leary
77c899d325 Merge pull request #858 from stleary/cleanup-after-commit
cleanup-after-commit for #854 and #856
2024-02-17 16:23:51 -06:00
Sean Leary
f164b8c597 cleanup-after-commit reverted pom.xml version 8 change and tabs in cdl. Updated JavaDocs in cdl 2024-02-13 20:08:54 -06:00
Sean Leary
6358b7f681 Merge pull request #854 from jscrdev/fixed-javadocs
Enhanced documentation for Java classes
2024-02-13 09:05:48 -06:00
Sean Leary
8550175556 Merge pull request #856 from michael-ameri/custom-delimiter
add ability for custom delimiters
2024-02-13 08:21:43 -06:00
mameri
72214f1b43 add ability for custom delimiters 2024-02-09 11:52:18 +01:00
Valentyn Kolesnikov
99c84fdf3a Enhanced documentation for Java classes 2024-02-07 14:43:44 +02:00
Sean Leary
010e83b925 Update RELEASES.md for release 20240205 2024-02-05 20:44:18 -06:00
Sean Leary
9865dbbebe Update pom.xml for release 20240205 2024-02-05 20:23:59 -06:00
Sean Leary
4548696c8d Update README.md for release 20240205 2024-02-05 20:22:23 -06:00
Sean Leary
f0308a3475 Merge pull request #855 from stleary/Fix-stack-overflow-failures
Fixing JSONArrayTest testRecursiveDepthArrayFor1000Levels()
2024-02-05 14:18:20 -06:00
Sean Leary
19dec1bb5f Fixing JSONArrayTest testRecursiveDepthArrayFor1000Levels() 2024-02-02 13:11:37 -06:00
Sean Leary
f2d20988de Merge pull request #832 from keatontaylor10/feature-disable-whitespace-trim
Add a config flag to disable whitespace trimming
2024-01-26 19:40:36 -06:00
Keaton
7915d8518f Merge branch 'stleary:master' into feature-disable-whitespace-trim 2024-01-18 09:17:26 +02:00
Sean Leary
55b824d4c4 Merge pull request #846 from stleary/cleanup-and-merge-tests
Cleanup warnings and merge new unit tests
2024-01-04 08:52:02 -06:00
Sean Leary
ac7806d060 Merge pull request #845 from seppl831/fix-annotation-search-performance
improved annotation search performance
2024-01-01 11:43:15 -06:00
Sean Leary
86bb0a1a02 cleanup-and-merge-tests: pull in unit tests from #809 2023-12-30 17:00:02 -06:00
Sean Leary
5ddb8c3d35 cleanup-and-merge-tests: fix warnings, set gradlew permissions, enable unchecked warnings in maven 2023-12-30 16:30:19 -06:00
Thomas Gress
23ac2e7bca improved annotation search performance 2023-12-29 12:28:24 +01:00
Sean Leary
d7819a4fa2 Merge pull request #823 from sk02241994/issue_743
JSON parsing self reference object and array
2023-12-27 05:28:17 -06:00
sk02241994
7701f21839 Adding comments 2023-12-24 11:39:26 +05:30
sk02241994
ffd48afa42 Review comments 2023-12-23 10:53:54 +05:30
sk02241994
abea194120 Adding JSONParserConfiguration for configuring the depth of nested maps 2023-12-22 15:47:55 +05:30
sk02241994
dcac3bc18e Adding test case for nested json with depth of 999, 1000, 1001 2023-12-22 15:47:54 +05:30
sk02241994
6d811607dd Resolving issue #743
- Recursive depth issue found in JSONObject
- Recursive depth issue found in JSONArray
2023-12-22 15:47:54 +05:30
Keaton Taylor
4d6de8c00a Remove unused constructor and add comment above other constructor 2023-12-13 14:04:05 +02:00
Sean Leary
6dba7220e1 Merge pull request #835 from LaFriska/branch1
deleted redundant .toString() call in README test method Sysout
2023-12-07 08:20:34 -06:00
Sean Leary
d4521696a9 Merge pull request #831 from adityap27/refactor
Refactor NumberConversionUtil and toString() of CookieList & XML Classes.
2023-12-03 10:18:22 -06:00
Keaton Taylor
e430db40aa Update XMLParserConfiguration to not be static and add a comment about the use of shouldTrimWhiteSpace 2023-11-30 10:05:54 +02:00
LaFriska
7cbeb35498 deleted redundant .toString() call in README test method Sysout 2023-11-28 17:39:46 -05:00
Aditya Purohit
aba82d9cc4 isNumericChar() - switch comparison order 2023-11-28 02:56:10 +00:00
Keaton Taylor
9ee10fdfc8 Merge remote-tracking branch 'origin/master' into feature-disable-whitespace-trim 2023-11-27 11:15:49 +02:00
Aditya Purohit
4a468d163a Merge branch 'stleary:master' into refactor 2023-11-26 20:16:53 -04:00
Sean Leary
92991770ca Merge pull request #830 from HappyHacker123/upgrade_json_path
Upgrade json-path's version to 2.4.0 to avoid dependency conflict.
2023-11-25 13:51:07 -06:00
Keaton Taylor
09f35372d4 Update clone() method so that default constructor does not need to be changed 2023-11-22 11:14:50 +02:00
Keaton Taylor
30f5b2de79 Add a config flag to disable whitespace trimming 2023-11-20 17:50:22 +02:00
Aditya Purohit
7f1cb8bf62 refactor: decompose condition of digit checks by using extra method 'isNumericChar(...)' in NumberConversionUtil. 2023-11-19 09:51:44 -04:00
Aditya Purohit
75419e3f25 refactor: introduce explaining variable 'indentationSuffix' in XML.toString() 2023-11-19 09:21:05 -04:00
Aditya Purohit
097a401f3f refactor: rename variable boolean 'b' to 'isEndOfPair' in CookieList.toString() 2023-11-19 09:11:32 -04:00
Sean Leary
5c4a7a1b1f Merge pull request #828 from harshith2000/Fix-flaky-tests
Fixed flaky tests in XMLTest.java
2023-11-17 11:51:00 -06:00
HappyHacker123
b5f9febfe9 Upgrade json-path's version to 2.4 to avoid dependency conflict. 2023-11-17 21:31:06 +08:00
Saiharshith Karuneegar Ramesh
1a61af8255 Fixed flaky tests in XMLTest.java 2023-11-13 13:25:30 -06:00
Sean Leary
11c29c366d Merge pull request #824 from johnjaylward/fix821
Ignore tests that fail due to resource differences
2023-11-10 11:27:18 -06:00
John J. Aylward
a3742acf74 Fixes #821
add ignore annotation to tests that may fail due to differences in machine resources and can't be controlled via the tests
2023-11-06 17:54:09 -05:00
Sean Leary
783d298f99 Merge pull request #814 from rudrajyotib/issue813
Refactor duplicate code for stringToNumber() in JSONObject, JSONArray, and XML
2023-10-31 17:40:17 -05:00
Sean Leary
6c1bc0660a Merge pull request #820 from rudrajyotib/issue748
Close XML tag explicitly for empty tags with configuration.
2023-10-31 17:38:45 -05:00
rudrajyoti biswas
8ec822c575 #748 - PR comments - follow convention of configuration builder. 2023-10-28 07:36:31 +05:30
rudrajyoti biswas
1ceb70b525 #813 - PR comments - alignments 2023-10-28 07:09:37 +05:30
rudrajyoti biswas
c05d7058ff #748 - javadoc updated for methods. 2023-10-27 17:17:20 +05:30
rudrajyoti biswas
07a358449a Merge branch 'master' into issue813 2023-10-27 17:09:01 +05:30
rudrajyoti biswas
7fe2fd95a5 Merge branch 'master' into issue748 2023-10-27 17:07:46 +05:30
Sean Leary
b5b9f636ff Merge pull request #815 from johnjaylward/ensure_1-6_compat
Ensure java 6 compatable
2023-10-25 13:53:23 -05:00
rudrajyoti biswas
c6ec2f0e4c #748 - close XML tag explicitly for empty tags with configuration. 2023-10-25 23:23:00 +05:30
Sean Leary
1a2108efa2 Merge pull request #812 from yeikel/patch-1
docs: use syntax highlighting
2023-10-25 09:58:30 -05:00
Sean Leary
caadcba30e Merge pull request #794 from rudrajyotib/master
XML optLong/getLong equivalent updates for string to number conversion.
2023-10-25 09:55:13 -05:00
John J. Aylward
ea842b437c remove unneeded matrix build typ for java 1.6 2023-10-23 17:13:07 -04:00
John J. Aylward
a2a8240d0d upload jar files to GitHub release 2023-10-23 17:07:10 -04:00
John J. Aylward
1ab11d0802 ensure java 6 compatable 2023-10-23 15:08:21 -04:00
rudrajyoti biswas
5539722c69 #813 - address PR review comment - brought down visibility. 2023-10-23 23:03:35 +05:30
rudrajyoti biswas
98b79ae7bf #813 - moved number conversion related common changes to utility static method. 2023-10-23 19:16:25 +05:30
Rudrajyoti Biswas
04a4c5a3ec Merge branch 'stleary:master' into master 2023-10-21 12:29:31 +00:00
Yeikel
6007165c17 docs: use syntax highlighting
use syntax highlighting to improve the format of the readme
2023-10-21 00:10:42 -04:00
Sean Leary
411f71137b Merge pull request #803 from yeikel/patch-2
ci: test with Java 21
2023-10-20 07:37:32 -05:00
Sean Leary
e9117dbe5c Merge branch 'master' into patch-2 2023-10-20 07:33:03 -05:00
Sean Leary
7a85b514a9 Merge pull request #808 from theKnightsOfRohan/fix-build-messages
Fix compiler warnings
2023-10-20 07:32:07 -05:00
Sean Leary
82c8f486c5 Merge pull request #806 from johnjaylward/deploymentPipeline
Add new deployment pipeline
2023-10-20 07:31:12 -05:00
Sean Leary
006b29bda3 Merge branch 'master' into deploymentPipeline 2023-10-20 07:28:44 -05:00
Sean Leary
996d3a5fad Merge pull request #801 from johnjaylward/updateActionsForDeploy
Updates the pipeline to validate that packaging a jar works properly
2023-10-20 07:26:41 -05:00
Yeikel
de745e9c81 ci: test with Java 21 2023-10-19 22:41:47 -04:00
rudrajyoti biswas
2374766018 #790 - Update XML with changes for string to number conversion.
For now the code remains duplicated in JSON and XML parsers.
Unit test cases updated to comply with number expectations.
2023-10-19 14:07:53 +05:30
rudrajyoti biswas
1d0775cce7 Revert changes with feature and refactor together. 2023-10-19 10:28:11 +05:30
Sean Leary
d677a99f4e Merge pull request #798 from hofi1/bugfix/fix-XML-flacky-test
fix: flakiness in org.json.junit.XMLTest#testIndentComplicatedJsonObjectWithArrayAndWithConfig
2023-10-17 13:52:29 -05:00
Sean Leary
e6d37c469d Merge pull request #788 from hofi1/bugfix/fix-JSON-flakiness
Fix string compare unit tests
2023-10-17 13:51:40 -05:00
theKnightsOfRohan
f074bed732 fix(ParserConfiguration): add <T> params to docs 2023-10-16 17:48:03 -07:00
John J. Aylward
e8f125fb6e update workflow to use GPG 2023-10-16 18:23:39 -04:00
John J. Aylward
ed183e6142 remove deprecated parent pom per Sonatype docs 2023-10-16 18:22:08 -04:00
John J. Aylward
a86786a5f5 Add snapshot repository 2023-10-16 18:03:39 -04:00
John J. Aylward
7c4f98c42c Add new deployment pipeline.
This should only trigger when a release is published
2023-10-16 17:30:57 -04:00
John J. Aylward
3894483560 Add build badges to README 2023-10-16 15:44:02 -04:00
John J. Aylward
be115059e9 Correct supported java versions 2023-10-16 15:44:02 -04:00
John J. Aylward
2b41cf44b5 include jar in job artifacts 2023-10-16 15:43:55 -04:00
John J. Aylward
9a9efac2af Correct moditect configuration to work on java8 2023-10-16 15:36:33 -04:00
John J. Aylward
134074aeaa Revert "Reverting #761"
This reverts commit b180dbedbc.
2023-10-16 15:36:33 -04:00
John J. Aylward
8540bb80c0 Validate that the mvn package step completes 2023-10-16 15:36:33 -04:00
simonh5
4dfd779b1c fix: flakiness in org.json.junit.XMLTest#testIndentComplicatedJsonObjectWithArrayAndWithConfig 2023-10-14 17:21:06 -05:00
rudrajyoti biswas
7b2677ac5a #790 - Update XML with changes for string to number conversion.
Moved the code logic to a common utility to de-duplicate.
2023-10-14 10:05:36 +05:30
simonh5
29a7f4622d remove JSONAssert 2023-10-13 21:23:09 -05:00
Sean Leary
f346203cd6 Merge pull request #793 from stleary/revert-761
Reverting #761
2023-10-13 16:10:17 -05:00
Sean Leary
b180dbedbc Reverting #761 2023-10-13 16:04:14 -05:00
Sean Leary
cca6d1020f Merge pull request #792 from stleary/pre-release-20231013
update the docs for release 20231013
2023-10-13 15:40:19 -05:00
Sean Leary
af5f780d5b update the docs for release 20231013 2023-10-13 15:30:31 -05:00
Sean Leary
495cec9037 Merge pull request #783 from rudrajyotib/master
optLong vs getLong inconsistencies
2023-10-12 22:06:11 -05:00
simonh5
e4aa7f1308 fix: change from JSONAssert to checking the similarity of JSONObjects 2023-10-12 21:09:27 -05:00
rudrajyoti biswas
56cb5f84c4 #653 - review comments updated. 2023-10-12 11:03:13 +05:30
rudrajyoti biswas
0cdc38ac24 #653 - review comments updated. 2023-10-12 00:53:36 +05:30
Rudrajyoti Biswas
d5277b126b Merge branch 'stleary:master' into master 2023-10-11 19:17:21 +00:00
Simon Hofbauer
228598ca84 Merge pull request #6 from hofi1/bugfix/fix-flakyness-testToJSONObject_reversibility
fix: flakiness in JSONMLTest#testToJSONObject_reversibility
2023-10-10 18:20:20 -05:00
Simon Hofbauer
0a6fb1d578 Merge pull request #5 from hofi1/bugfix/fix-flakyness-org.json.junit.JSONObjectTest#valueToString
fix: flakiness in org.json.junit.JSONObjectTest#valueToString
2023-10-10 18:19:28 -05:00
Sean Leary
c4cd526c53 Merge pull request #779 from Madjosz/713_jsonobject_nonfinite
add validity check for JSONObject constructors
2023-10-08 17:08:32 -05:00
Sean Leary
776b5ccb85 Merge pull request #778 from Madjosz/fix_xml_test
Fix XMLTest.testIndentComplicatedJsonObjectWithArrayAndWithConfig() for Windows - in the test
2023-10-08 17:06:55 -05:00
Sean Leary
fb99c06bad Merge branch 'master' into fix_xml_test 2023-10-08 17:03:35 -05:00
Sean Leary
bc09f90e90 Merge pull request #782 from mureinik/XMLTest-windows
Fix XMLTest.testIndentComplicatedJsonObjectWithArrayAndWithConfig() for Windows - in the test
2023-10-08 17:00:25 -05:00
Madjosz
c93014cb53 add validity check for JSONObject constructors
* fixes #713
* document JSONException in JavaDoc
* remove unused Comparable<T> boundary to reuse GenericBean in test
2023-10-07 09:38:54 +02:00
Madjosz
0e4a94d91d fix failing test XML test on Windows machines 2023-10-07 09:38:07 +02:00
rudrajyoti biswas
1a38879c90 #653 - optLong vs getLong inconsistencies
For exponential decimal conversion, number is not touched.
Leading zeros removed from numeric number strings before converting to number.
2023-10-06 21:34:00 +05:30
Allon Mureinik
4c8cac22a8 Use System.lineSeparator()
Use the built-in System.lineSeparator() instead of implementing it
ourselves with System.getProperty("line.separator") in order to clean
up the code and make it easier to maintain.
2023-10-05 19:47:33 +03:00
Allon Mureinik
fe45fa9cfb Fix XMLTest on Windows
XMLTest.testIndentComplicatedJsonObjectWithArrayAndWithConfig fails
when run on Windows due to mismatching linebreaks (that aren't
important for the test's functionality) between the actual and
expected strings.

For the actual strings, linebreaks are canonized to the platform's
native linebreak using `replaceAll("\\n|\\r\\n",
System.getProperty("line.separator")`. However, the expected result is
read from a file, and is left with the linebreaks that were originally
used to create it.

The solution is to perform the same canonization on both strings.

Closes #781
2023-10-05 15:36:07 +03:00
Sean Leary
79af389f7a Merge pull request #774 from mccartney/removing-synchronized
Removing excessive synchronization
2023-10-04 07:40:10 -05:00
Sean Leary
1726b6cf55 Merge pull request #776 from mccartney/junit-4-13-2
JUnit 4.13.2
2023-10-04 07:39:26 -05:00
Sean Leary
beb2fb5706 Merge pull request #772 from eamonnmcmanus/complexkey
Disallow nested objects and arrays as keys in objects.
2023-10-01 11:04:40 -05:00
Grzegorz Olędzki
ff921db783 Junit 4.13.2 2023-09-30 21:53:36 +02:00
Grzegorz Olędzki
61bb60e752 Removing excessive synchronization 2023-09-30 21:36:11 +02:00
Sean Leary
ef68cdf810 Merge pull request #773 from eedijs/master
Add optJSONArray method to JSONObject with a default value
2023-09-30 11:47:01 -05:00
Éamonn McManus
eaa5611ba3 Merge branch 'stleary:master' into complexkey 2023-09-28 11:33:08 -07:00
Éamonn McManus
dbb113176b Add more test cases for unquoted text in objects and arrays. 2023-09-28 11:05:50 -07:00
Éamonn McManus
16967f322e Simplify the check for object keys that are themselves objects.
For object keys, we can just skip the part of `nextValue()` that parses values
that are objects or arrays. Then the existing logic for unquoted values will
already stop at `{` or `[`, and that will produce a `Missing value` exception.
2023-09-27 12:42:04 -07:00
Edijs
284a316838 Add optJSONArray and optJSONObject methods to JSONArray with a default value 2023-09-27 19:30:45 +03:00
Sean Leary
4e8231c512 Merge pull request #770 from eamonnmcmanus/testfixes
Small test fixes.
2023-09-25 20:37:44 -05:00
Edijs
db0fde2a56 Add optJSONArray method to JSONObject with a default value 2023-09-25 20:31:58 +03:00
Éamonn McManus
661114c50d Generalize the logic to disallow nested objects and arrays as keys in objects.
Fixes #771.
2023-09-20 10:50:48 -07:00
simonh5
ca88454f1c fix: flakiness in org.json.junit.JSONObjectTest#valueToString 2023-09-19 14:28:06 -05:00
Éamonn McManus
3e688afc66 Small test fixes.
One test method was missing `@Test` so it was never run.

One test method used another test class as the base for finding a test
resource. While this works in practice with Maven, it is not strictly
right.
2023-09-19 07:38:13 -07:00
simonh5
becc1631e6 fix: flakiness in JSONMLTest#testToJSONObject_reversibility 2023-09-18 20:20:13 -05:00
Sean Leary
01727fd0ed Merge pull request #769 from jscrdev/fixed-warnings
Addressed Java 17 compile warnings
2023-09-16 08:30:44 -05:00
Valentyn Kolesnikov
74cd73f97c Addressed compile warnings 2023-09-08 07:34:00 +03:00
Sean Leary
c29d4881e0 Merge pull request #741 from jscrdev/setup-java-11
Configure Java 8 as the minimum required version
2023-09-03 17:17:04 -05:00
Sean Leary
7c1b6531e7 Update CONTRIBUTING.md
Updated for Hacktoberfest 2023
2023-09-03 11:35:15 -05:00
Valentyn Kolesnikov
db122e5d3a Merge branch 'master' into setup-java-11 2023-09-03 18:40:40 +03:00
Sean Leary
a309931d20 Merge pull request #761 from bowbahdoe/master
Add module-info
2023-09-03 10:35:21 -05:00
Valentyn Kolesnikov
e27da22e05 Update build.gradle 2023-08-29 05:00:13 +03:00
Valentyn Kolesnikov
af6d07cecb Resolved Gradle build dependency 2023-08-29 03:22:20 +03:00
Sean Leary
64093366b3 Merge pull request #764 from johnjaylward/CodeQl_Pipeline_Update
Update CodeQL action version
2023-08-28 19:20:36 -05:00
John J. Aylward
9b69ec49ad update CodeQL action version 2023-08-28 12:51:52 -04:00
Valentyn Kolesnikov
2c674be1b6 Update pipeline.yml 2023-08-28 19:06:27 +03:00
Valentyn Kolesnikov
be33deb7d5 Update README.md 2023-08-28 19:04:30 +03:00
Valentyn Kolesnikov
48089a4da7 Update pipeline.yml 2023-08-28 19:04:30 +03:00
Valentyn Kolesnikov
a4e152f4f0 Update pipeline.yml 2023-08-28 19:03:43 +03:00
dburbrid
3dd8f2ecd5 Correction of bug when compiling/testing on Windows: Issue537 file must be read as UTF-8 (Issue 745) 2023-08-28 19:03:40 +03:00
Valentyn Kolesnikov
bae0b0dac9 Updated mockito 2023-08-28 19:01:47 +03:00
Valentyn Kolesnikov
e563dbcaaa Setup java 8 as minimum version 2023-08-28 19:00:00 +03:00
Ethan McCue
50dfcc59b3 Remove automatic module name 2023-08-16 11:25:15 -04:00
Ethan McCue
b2943eb395 Add module-info to maven build 2023-08-16 11:24:57 -04:00
Sean Leary
60662e2f83 Merge pull request #759 from eamonnmcmanus/eofnull
JSON parsing should detect embedded `\0` values
2023-08-05 08:33:26 -05:00
Éamonn McManus
2a4bc3420a Apply simplification suggested by @johnjaylward. 2023-08-01 14:38:45 -07:00
Éamonn McManus
b6ff0db984 Fix indentation in test. 2023-08-01 13:49:59 -07:00
Éamonn McManus
c8a9e15a57 Don't skip past \0 when parsing JSON objects.
A better solution might be to use -1 instead 0 to represent EOF everywhere,
which of course means changing `char` variables to `int`. The solution here is
enough to solve the immediate problem, though.

Fixes #758.
2023-08-01 13:11:25 -07:00
Sean Leary
402db6ad84 Merge pull request #753 from davejbur/add-object-methods-and-test
Updated new object methods
2023-07-14 20:03:13 -05:00
dburbrid
4951ec48c8 Renamed object methods from ...Obj to ...Object.
Added object method for optDoubleObject (returns Double vice double).
Added similar methods in JSONArray.
Added test methods.
2023-06-29 09:39:34 +01:00
Sean Leary
8ce0019a5d Merge pull request #752 from davejbur/issue-745-compile-error
Correction of bug when compiling/testing on Windows
2023-06-28 20:45:05 -05:00
dburbrid
3d524349a1 Correction of bug when compiling/testing on Windows: Issue537 file must be read as UTF-8 (Issue 745) 2023-06-26 09:33:03 +01:00
Sean Leary
a963115ac2 Update pom.xml for maven deploy
Deploy failed on the mac pro with: gpg: signing failed: Inappropriate ioctl for device

Somehow I had a different gpg version installed. This change fixed it.
2023-06-18 12:58:32 -05:00
Sean Leary
f959baa3cb Merge pull request #749 from stleary/pre-release-20230618
Prep for release 20230618
2023-06-18 12:24:27 -05:00
Sean Leary
c048b36516 Update pom.xml for 20230618 2023-06-18 12:18:36 -05:00
Sean Leary
f6e5bfa2db Update RELEASES.md for 20230618 2023-06-18 12:17:56 -05:00
Sean Leary
084b24cbe7 Update README.md for 20230618 2023-06-18 12:16:14 -05:00
Sean Leary
22ccf1b8e5 Merge pull request #740 from michael-o/proper-encoding
JSONTokener(InputStream) violates rfc8259#section-8.1 (#739)
2023-05-28 14:11:41 -05:00
Michael Osipov
133c0cc75f JSONTokener(InputStream) violates rfc8259#section-8.1 (#739)
Always use UTF-8 when an InputStream is passed.

This fixes #739.
2023-05-24 11:45:25 +02:00
Sean Leary
0578285036 Merge pull request #734 from superMaaax/fix_flaky
Fixed Flaky Tests Caused by JSON permutations
2023-03-27 16:25:57 -05:00
superMaaax
48fb5261fe Fixed Flaky Tests Caused by JSON permutations
###Description
Flaky Tests found using NonDex by running the commands -
mvn -pl . edu.illinois:nondex-maven-plugin:2.1.1:nondex -Dtest=org.json.junit.XMLTest#testIndentComplicatedJsonObject

mvn -pl . edu.illinois:nondex-maven-plugin:2.1.1:nondex -Dtest=org.json.junit.XMLTest#testIndentSimpleJsonArray

mvn -pl . edu.illinois:nondex-maven-plugin:2.1.1:nondex -Dtest=org.json.junit.XMLTest#testIndentSimpleJsonObject

The logged failure was-

[ERROR] Failures:
[ERROR] XMLTest.testIndentSimpleJsonObject:1193 expected:<...>
<employee>
<[married>true</married>
<name>sonoo</name>
<salary>56000</salary]>
</employee>
</Te...> but was:<...>
<employee>
<[name>sonoo</name>
<salary>56000</salary>
<married>true</married]>
</employee>
</Te...>

The issue is the same for all three tests, so here I only show the failure message for the third test (to reduce the length of the error message).

### Investigation

The tests fail with a comparison error while comparing an expected JSON String and the result from the value returned from XML.toString(). The toString function of XML makes no guarantees as to the iteration order of the attributes in the object. This makes the test outcome non-deterministic, and the test fails whenever the function returns a mismatch in order of the elements in the JSON String. To fix this, the expected and actual keys should be checked in a more deterministic way so that the assertions do not fail.

### Fix

Expected and Actual values can be converted into JSONObject and the similar function can be used to compare these objects. As this function compares the values inside the JSONObjects without needing order, the test becomes deterministic and ensures that the flakiness from the test is removed.

The PR does not introduce a breaking change.
2023-03-21 20:58:32 -05:00
Sean Leary
8353b9c3f0 Merge pull request #731 from JoaoGFarias/patch-1
Removing commented out code in JSONObject optDouble()
2023-03-11 19:28:01 -06:00
Sean Leary
fe22b242b5 Merge pull request #733 from haribabu-dev/jsontokener_closable
JSONTokener implemented java.io.Closeable
2023-03-11 19:26:55 -06:00
HariBabu t
7eca507d13 Removed overriding closable interface. 2023-03-07 13:58:30 +08:00
HariBabu t
e1eabc9c27 JSONTokener implemented java.io.Closeable 2023-03-04 23:08:32 +08:00
JoĂŁo Farias
0d436d92e2 Removing commented out code 2023-03-02 16:39:11 +01:00
Sean Leary
45bcba518f Merge pull request #729 from TamasPergerDWP/f-parserconfig
Refactor ParserConfiguration class hierarchy
2023-02-28 12:49:42 -06:00
Sean Leary
47fb49b6a8 Update for release 20230227 2023-02-27 07:21:11 -06:00
Sean Leary
0df034c9fd Update for release 20230227 2023-02-27 07:20:10 -06:00
Sean Leary
f0a05e6911 Update README.md 2023-02-27 07:17:51 -06:00
Tamas Perger
24093491a8 refactor: introduce ParserConfiguration class hierarchy 2023-02-21 19:13:07 +00:00
Sean Leary
1275f6809d Merge pull request #723 from TamasPergerDWP/master
JSONML should be protected from stack overflow exceptions caused by recursion, resolving #722
2023-02-17 13:47:36 -06:00
Tamas Perger
9234eab00a refactor: make JSONMLParserConfiguration all-args constructor private, enforcing the builder pattern. 2023-02-13 01:09:29 +00:00
Tamas Perger
72f4c3e646 refactor: rename XMLtoJSONMLParserConfiguration to JSONMLParserConfiguration 2023-02-12 01:32:34 +00:00
Tamas Perger
df2d6f8363 fix: introduce optional XMLtoJSONMLParserConfiguration parameter for JSONML.toJSONArray(...) functions, to facilitate max nesting depth override. 2023-02-11 01:52:13 +00:00
Tamas Perger
a6e412bded fix: limit the nesting depth in JSONML
Limit the XML nesting depth for CVE-2022-45688 when using the JsonML transform.
2023-02-10 01:46:44 +00:00
Tamas Perger
2391d248cc fix: amend XMLParserConfiguration.clone() to include the new maxNestingDepth param.
Amend Javadoc for XML and XMLParserConfiguration classes.
2023-02-10 01:45:34 +00:00
Sean Leary
401495ae86 Merge pull request #720 from cleydyr/issue-708
Limit the XML nesting depth for CVE-2022-45688
2023-02-05 19:30:04 -06:00
Cleydyr de Albuquerque
448e204186 docs: remove wrong description of parse method 2023-02-02 20:16:16 +01:00
Cleydyr de Albuquerque
eb56704e68 fix: set default maximum nesting depth as 512 2023-02-02 18:15:03 +01:00
Cleydyr de Albuquerque
651511f500 tests: add new test to verify that an XML having the permitted nesting depth can be converted 2023-02-01 20:22:47 +01:00
Cleydyr de Albuquerque
a14cb12c85 refactor: keep consistence with other tests and tidy up constant 2023-02-01 20:22:32 +01:00
Cleydyr de Albuquerque
f566a1d9ee fix: limit the nesting depth 2023-02-01 16:26:58 +01:00
Sean Leary
5920eca2d7 Merge pull request #711 from 6d64/revert-pull-707-interviewbit-spam
Revert pull 707 - interviewbit spam
2022-11-30 20:20:39 -06:00
6d64
3b097d051a Revert pull 707 - interviewbit spam
Reverted commit that was added by a bot adding interviewbit spam to
repos on github
2022-12-01 03:21:26 +11:00
Sean Leary
4e630e58a4 Merge pull request #707 from ASAlishaa/patch-1
Added new resource to the repos
2022-11-17 19:14:11 -06:00
ASAlisha
b732188e4e Added new resource to this repos.
Added resource in the correct format.
2022-11-15 16:31:05 +05:30
ASAlisha
5369442671 Added new resource to the repos
Added new useful JSON resource.
2022-11-14 03:26:18 +05:30
Sean Leary
bb1138762a Merge pull request #703 from TomerPacific/feature/update-release-for-JSONMap-Change
Update Releases.md for JSONObject(Map): Throws NPE if key is null
2022-11-05 17:39:13 -05:00
Sean Leary
6a732ec99d Merge pull request #704 from niranjanib/fix-javadoc-not-visible-in-website
move javadoc comments above the interface definition to make it visible
2022-11-05 17:38:15 -05:00
Niranjani
c798c76ddd move javadoc comments above the interface definition to make it visible
Fix #670
2022-10-30 22:10:38 +05:30
unknown
23d5e52a53 feature/update-release-for-JSONMap-Change adding breaking change for JSONMap to corresponding release 2022-10-28 08:45:54 +03:00
Sean Leary
98df35449a Merge pull request #696 from bmk15897/fix-flaky-test
Update JSONPointerTest for NonDex compatibility
2022-10-15 08:24:01 -05:00
Sean Leary
1be6ee31a7 Merge pull request #694 from DeaneOC/Pretty-Print-XML-Functionality
Pretty print XML
2022-10-13 21:46:26 -05:00
Bharati Kulkarni
a2d3d3c9b5 Fix Flaky Test 2022-10-11 14:33:43 -05:00
Dean
bf9219386a Merge branch 'master' of https://github.com/stleary/JSON-java into Pretty-Print-XML-Functionality 2022-10-10 11:12:49 +01:00
Dean
85495facbd Corrected test 2022-10-10 11:12:35 +01:00
Dean
7aba3ac941 System line seperator now being used in JUnit test 2022-10-10 11:09:42 +01:00
Sean Leary
d51250f6b0 Merge pull request #692 from InACommandBlock/patch-1
Example.md syntax highlight and indentation
2022-10-09 22:28:36 -05:00
Dean
9cb8e153bf Added JavaDocs 2022-10-07 17:57:07 +01:00
Dean
80c1479ad8 Merge branch 'master' of https://github.com/stleary/JSON-java into Pretty-Print-XML-Functionality 2022-10-07 17:56:54 +01:00
Sean Leary
444335d12a Merge pull request #691 from hendrixjoseph/issue-657-unit-tests-should-check-various-number-formats
create unit tests for various number formats
2022-10-07 11:18:41 -05:00
Dean
a2c0562e04 Removed unused import 2022-10-07 15:04:09 +01:00
Dean
153972afdf Adding resources 2022-10-07 10:35:14 +01:00
Dean
4a8ff28fd8 Reduced Test code length by using resources 2022-10-07 10:35:06 +01:00
Dean
fa457a4113 Test cases for XML toString indentation 2022-10-06 12:01:26 +01:00
Dean
b7f708b222 Altered XML toString to allow indentation param 2022-10-06 12:01:13 +01:00
TheCommandBlock
12411b7981 Update Examples.md
Co-authored-by: JAYSE <104235500+JayseMayne@users.noreply.github.com>
2022-10-06 03:18:03 +02:00
TheCommandBlock
61801c623e Minor Adjustments Example.md
Added syntax highlighting, standardised indentation
2022-10-06 00:48:34 +02:00
hendrixjoseph
1915aab7c4 create unit tests for various number formats 2022-10-04 14:32:41 -04:00
Sean Leary
8439039da7 Update pom.xml
For the 20220924 release
2022-09-24 16:25:18 -05:00
Sean Leary
a6bdd081eb Update RELEASES.md 2022-09-24 16:13:11 -05:00
Sean Leary
a30d71fdca Update README.md 2022-09-24 16:11:12 -05:00
43 changed files with 4972 additions and 834 deletions

View File

@@ -25,11 +25,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
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@v1
uses: github/codeql-action/analyze@v2

View File

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

2
.gitignore vendored
View File

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

View File

@@ -1,8 +1,8 @@
# Contribution Guidelines
Feel free to work on any issue with a #hacktoberfest label.
Feel free to work on any open issue, you don't need to ask permission first. This year, the hacktoberfest label will be added to any PR and associated issue during the month of October.
If you discover an issue you would like to work on, you can add a new issue to the list. If it meets our criteria, a hacktoberfest label will be added.
If you discover an issue you would like to work on, you can add a new issue to the list. If it meets our criteria, it will be available to work on (if not, it will be closed after review).
# Who is allowed to submit pull requests for this project?

View File

@@ -1,7 +1,7 @@
<h1>Examples</h1>
<p>Imports used in the examples: </p>
```
```java
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -14,8 +14,8 @@ import java.util.Properties;
<h3>Using JSONArray</h3>
```
private static void JSONExampleArray1() {
```java
private static void JSONExampleArray1() {
//We create a JSONObject from a String containing an array using JSONArray
//Firstly, we declare an Array in a String
@@ -44,21 +44,21 @@ import java.util.Properties;
//Now, we construct the JSONObject using both the value array and the label array.
JSONObject object = array.toJSONObject(list);
System.out.println("Final JSONOBject: " + object);
}
}
//This method creates an JSONArray of labels in which those are generated by their positions
//This method creates an JSONArray of labels in which those are generated by their positions
private static JSONArray listNumberArray(int max){
private static JSONArray listNumberArray(int max){
JSONArray res = new JSONArray();
for (int i=0; i<max;i++) {
//The value of the labels must be an String in order to make it work
res.put(String.valueOf(i));
}
return res;
}
}
```
```
private static void JSONExampleArray2() {
```java
private static void JSONExampleArray2() {
//We can also create an Array without a String by creating an empty array and adding elements to it
@@ -76,13 +76,13 @@ import java.util.Properties;
JSONArray list = listNumberArray(array.length());
JSONObject object = array.toJSONObject(list);
System.out.println("Final JSONOBject: " + object);
}
}
```
<h3>Using JSONStringer</h3>
```
private static void JSONExampleStringer() {
```java
private static void JSONExampleStringer() {
//We initializate the JSONStringer
@@ -112,12 +112,12 @@ import java.util.Properties;
JSONObject jsonObject = new JSONObject(str);
System.out.println("Final JSONOBject: " + jsonObject);
}
}
```
<h3>Using JSONObject</h3>
```
private static void JSONExampleObject1() {
```java
private static void JSONExampleObject1() {
//We can create a JSONObject from a String with the class builder
@@ -125,10 +125,10 @@ import java.util.Properties;
JSONObject example = new JSONObject(string);
System.out.println("Final JSONObject: " + example);
}
}
```
```
private static void JSONExampleObject2() {
```java
private static void JSONExampleObject2() {
//We can also create a JSONObject directly without messing around with any of the other functions.
@@ -149,10 +149,10 @@ import java.util.Properties;
//example.put("nullValue", null); //This is not possible
System.out.println("Final JSONOBject: " + example);
}
}
```
```
private static void JSONExampleObject3() {
```java
private static void JSONExampleObject3() {
//We can also create a JSONObject with a Java Map
//YoU will need a Map whose keys are Strings. The values can be whatever you want
@@ -166,12 +166,12 @@ import java.util.Properties;
JSONObject example = new JSONObject(map);
System.out.println("Final JSONOBject: " + example);
}
}
```
<h3>Using JSONWriter</h3>
```
private static void JSONExamplWriter() {
```java
private static void JSONExamplWriter() {
//This method works in a very similar way to Object and Stringer in the construction of the JSON.
//The difference is that it needs a Java object called "Appendable" like StringBuilder
@@ -200,10 +200,10 @@ import java.util.Properties;
//The difference is that we don't get a JSONObject in this one.
}
}
```
```
private static void JSONExampleTokener() {
```java
private static void JSONExampleTokener() {
//A partir de una String podemos crear un JSONTokener, que lo podemos usar alternativamente para JSONArray,JSONObject
@@ -215,7 +215,7 @@ import java.util.Properties;
JSONObject object = new JSONObject(token);
JSONArray array = new JSONArray(token);
}
}
```
<h2>Part 2: Conversion methods</h2>
<p>We don't need to have a JSON document to work. This project also admits conversions from other type of files.</p>
@@ -223,8 +223,8 @@ import java.util.Properties;
<h3>Extra: Conversion to JSONArray</h3>
```
private static void JSONObjectToArray() {
```java
private static void JSONObjectToArray() {
//We start with a JSONObject
String string = "{\"0\":\"value\",\"1\":5,\"2\":-2.345E68,\"3\":true}";
@@ -240,12 +240,12 @@ import java.util.Properties;
JSONArray array = example.toJSONArray(keyStrings);
System.out.println("Final JSONArray: " + array);
}
}
```
<h3>XML Conversions</h3>
```
private static void XMLToExampleConversion() {
```java
private static void XMLToExampleConversion() {
//We start with a JSONObject
@@ -256,10 +256,10 @@ import java.util.Properties;
String output = XML.toString(example);
System.out.println("Final XML: " + output);
}
}
```
```
private static void XMLFromExampleConversion() {
```java
private static void XMLFromExampleConversion() {
//We start with a string with the XML format
@@ -270,12 +270,12 @@ import java.util.Properties;
JSONObject output = XML.toJSONObject(string);
System.out.println("Final JSONObject: " + output);
}
}
```
<h3>Cookie Conversions</h3>
```
private static void CookieToExampleConversion() {
```java
private static void CookieToExampleConversion() {
//We start with a JSONObject
//The JSONOBject needs to entries that gives the cookie a name and gives the field "name" a name too.
@@ -288,10 +288,10 @@ import java.util.Properties;
String output = Cookie.toString(example);
System.out.println("Final Cookie: " + output);
}
}
```
```
private static void CookieFromExampleConversion() {
```java
private static void CookieFromExampleConversion() {
//We start with a string with the Cookie format
@@ -301,13 +301,13 @@ import java.util.Properties;
JSONObject output = Cookie.toJSONObject(string);
System.out.println("Final JSONObject: " + output);
}
}
```
<h3>HTTP Conversions</h3>
```
private static void HTTPToExampleConversion() {
```java
private static void HTTPToExampleConversion() {
//We start with a JSONObject
//The JSONObject must have the minimun header for a HTTP request or header
@@ -320,10 +320,10 @@ import java.util.Properties;
String output = HTTP.toString(example);
System.out.println("Final HTTP: " + output);
}
}
```
```
private static void HTTPFromExampleConversion() {
```java
private static void HTTPFromExampleConversion() {
//We start with a string with the HTTP format
@@ -333,11 +333,11 @@ import java.util.Properties;
JSONObject output = HTTP.toJSONObject(string);
System.out.println("Final JSONObject: " + output);
}
}
```
<h3>CDL Conversions</h3>
```
```java
private static void CDLToExampleConversion() {
//We start with some JSONObjects with the same values in the keys but different values in the "values"
@@ -358,9 +358,9 @@ private static void CDLToExampleConversion() {
String output = CDL.toString(array);
System.out.println("Final CDL: \r\n" + output);
}
```
}
```
```java
private static void CDLFromExampleConversion() {
//We start wtih a String with the CDL format
@@ -377,8 +377,8 @@ private static void CDLFromExampleConversion() {
```
<h3>Properties Conversions</h3>
```
private static Properties PropertyToExampleConversion() {
```java
private static Properties PropertyToExampleConversion() {
//We start with a JSONObject
@@ -391,10 +391,10 @@ private static void CDLFromExampleConversion() {
System.out.println("Final Properties: " + output);
return output;
}
}
```
```
private static void PropertyFromExampleConversion() {
```java
private static void PropertyFromExampleConversion() {
//We start with a Properties object
@@ -404,12 +404,12 @@ private static void CDLFromExampleConversion() {
JSONObject output = Property.toJSONObject(input);
System.out.println("Final JSONObject: " + output);
}
}
```
<h2>List of all examples methods</h2>
<h2>List of all examples methods</h2>
```
public static void main(String[] args) {
```java
public static void main(String[] args) {
//JSONObjectToArray();
//JSONExampleArray1();
//JSONExampleArray2();
@@ -428,6 +428,6 @@ private static void CDLFromExampleConversion() {
//CDLFromExampleConversion();
//PropertyToExampleConversion();
//PropertyFromExampleConversion();
}
}
```

View File

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

View File

@@ -20,9 +20,9 @@ repositories {
}
dependencies {
testImplementation 'junit:junit:4.13.1'
testImplementation 'com.jayway.jsonpath:json-path:2.1.0'
testImplementation 'org.mockito:mockito-core:1.9.5'
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.jayway.jsonpath:json-path:2.4.0'
testImplementation 'org.mockito:mockito-core:4.2.0'
}
subprojects {
@@ -30,9 +30,9 @@ subprojects {
}
group = 'org.json'
version = 'v20211205-SNAPSHOT'
version = 'v20230618-SNAPSHOT'
description = 'JSON in Java'
sourceCompatibility = '1.7'
sourceCompatibility = '1.8'
configurations.all {
}

View File

@@ -5,6 +5,18 @@ 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)
~~~
20240303 Revert optLong/getLong changes, and recent commits.
20240205 Recent commits.
20231013 First release with minimum Java version 1.8. Recent commits, including fixes for CVE-2023-5072.
20230618 Final release with Java 1.6 compatibility. Future releases will require Java 1.8 or greater.
20230227 Fix for CVE-2022-45688 and recent commits
20220924 New License - public domain, and some minor updates
20220320 Wrap StackOverflow with JSONException
20211205 Recent commits and some bug fixes for similar()
@@ -18,6 +30,7 @@ and artifactId "json". For example:
20190722 Recent commits
20180813 POM change to include Automatic-Module-Name (#431)
JSONObject(Map) now throws an exception if any of a map keys are null (#405)
20180130 Recent commits

0
gradlew vendored Normal file → Executable file
View File

85
pom.xml
View File

@@ -3,7 +3,7 @@
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
<version>20240303</version>
<packaging>bundle</packaging>
<name>JSON in Java</name>
@@ -15,18 +15,12 @@
It also includes the capability to convert between JSON and XML, HTTP
headers, Cookies, and CDL.
This is a reference implementation. There is a large number of JSON packages
This is a reference implementation. There are a large number of JSON packages
in Java. Perhaps someday the Java community will standardize on one. Until
then, choose carefully.
</description>
<url>https://github.com/douglascrockford/JSON-java</url>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>9</version>
</parent>
<scm>
<url>https://github.com/douglascrockford/JSON-java.git</url>
<connection>scm:git:git://github.com/douglascrockford/JSON-java.git</connection>
@@ -53,23 +47,36 @@
</properties>
<distributionManagement>
<repository>
<id>ossrh</id>
<name>Central Repository OSSRH</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.1.0</version>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -79,7 +86,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.0.1</version>
<version>5.1.9</version>
<extensions>true</extensions>
<configuration>
<instructions>
@@ -93,16 +100,19 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<version>3.11.0</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-Xlint:unchecked</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -115,7 +125,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<version>3.5.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
@@ -131,7 +141,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
@@ -139,6 +149,12 @@
<goals>
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
</plugin>
@@ -153,17 +169,34 @@
<autoReleaseAfterClose>false</autoReleaseAfterClose>
</configuration>
</plugin>
<plugin>
<groupId>org.moditect</groupId>
<artifactId>moditect-maven-plugin</artifactId>
<version>1.0.0.Final</version>
<executions>
<execution>
<id>add-module-infos</id>
<phase>package</phase>
<goals>
<goal>add-module-info</goal>
</goals>
<configuration>
<jvmVersion>9</jvmVersion>
<module>
<moduleInfoSource>
module org.json {
exports org.json;
}
</moduleInfoSource>
</module>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.json</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
<version>3.3.0</version>
</plugin>
</plugins>
</build>

View File

@@ -5,15 +5,15 @@ Public Domain.
*/
/**
* This provides static methods to convert comma delimited text into a
* JSONArray, and to convert a JSONArray into comma delimited text. Comma
* This provides static methods to convert comma (or otherwise) delimited text into a
* JSONArray, and to convert a JSONArray into comma (or otherwise) delimited text. Comma
* delimited text is a very popular format for data interchange. It is
* understood by most database, spreadsheet, and organizer programs.
* <p>
* Each row of text represents a row in a table or a data record. Each row
* ends with a NEWLINE character. Each row contains one or more values.
* Values are separated by commas. A value can contain any character except
* for comma, unless is is wrapped in single quotes or double quotes.
* for comma, unless it is wrapped in single quotes or double quotes.
* <p>
* The first row usually contains the names of the columns.
* <p>
@@ -25,25 +25,30 @@ Public Domain.
*/
public class CDL {
/**
* Constructs a new CDL object.
*/
public CDL() {
}
/**
* Get the next value. The value can be wrapped in quotes. The value can
* be empty.
* @param x A JSONTokener of the source text.
* @param delimiter used in the file
* @return The value string, or null if empty.
* @throws JSONException if the quoted string is badly formed.
*/
private static String getValue(JSONTokener x) throws JSONException {
private static String getValue(JSONTokener x, char delimiter) throws JSONException {
char c;
char q;
StringBuilder sb;
do {
c = x.next();
} while (c == ' ' || c == '\t');
switch (c) {
case 0:
if (c == 0) {
return null;
case '"':
case '\'':
} else if (c == '"' || c == '\'') {
q = c;
sb = new StringBuilder();
for (;;) {
@@ -51,9 +56,9 @@ public class CDL {
if (c == q) {
//Handle escaped double-quote
char nextC = x.next();
if(nextC != '\"') {
if (nextC != '\"') {
// if our quote was the end of the file, don't step
if(nextC > 0) {
if (nextC > 0) {
x.back();
}
break;
@@ -65,13 +70,12 @@ public class CDL {
sb.append(c);
}
return sb.toString();
case ',':
} else if (c == delimiter) {
x.back();
return "";
default:
x.back();
return x.nextTo(',');
}
x.back();
return x.nextTo(delimiter);
}
/**
@@ -81,17 +85,28 @@ public class CDL {
* @throws JSONException if a called function fails
*/
public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException {
return rowToJSONArray(x, ',');
}
/**
* Produce a JSONArray of strings from a row of comma delimited values.
* @param x A JSONTokener of the source text.
* @param delimiter custom delimiter char
* @return A JSONArray of strings.
* @throws JSONException if a called function fails
*/
public static JSONArray rowToJSONArray(JSONTokener x, char delimiter) throws JSONException {
JSONArray ja = new JSONArray();
for (;;) {
String value = getValue(x);
String value = getValue(x,delimiter);
char c = x.next();
if (value == null ||
(ja.length() == 0 && value.length() == 0 && c != ',')) {
(ja.length() == 0 && value.length() == 0 && c != delimiter)) {
return null;
}
ja.put(value);
for (;;) {
if (c == ',') {
if (c == delimiter) {
break;
}
if (c != ' ') {
@@ -116,9 +131,23 @@ public class CDL {
* @return A JSONObject combining the names and values.
* @throws JSONException if a called function fails
*/
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x)
throws JSONException {
JSONArray ja = rowToJSONArray(x);
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) throws JSONException {
return rowToJSONObject(names, x, ',');
}
/**
* Produce a JSONObject from a row of comma delimited text, using a
* parallel JSONArray of strings to provides the names of the elements.
* @param names A JSONArray of names. This is commonly obtained from the
* first row of a comma delimited text file using the rowToJSONArray
* method.
* @param x A JSONTokener of the source text.
* @param delimiter custom delimiter char
* @return A JSONObject combining the names and values.
* @throws JSONException if a called function fails
*/
public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x, char delimiter) throws JSONException {
JSONArray ja = rowToJSONArray(x, delimiter);
return ja != null ? ja.toJSONObject(names) : null;
}
@@ -130,15 +159,27 @@ public class CDL {
* @return A string ending in NEWLINE.
*/
public static String rowToString(JSONArray ja) {
return rowToString(ja, ',');
}
/**
* Produce a comma delimited text row from a JSONArray. Values containing
* the comma character will be quoted. Troublesome characters may be
* removed.
* @param ja A JSONArray of strings.
* @param delimiter custom delimiter char
* @return A string ending in NEWLINE.
*/
public static String rowToString(JSONArray ja, char delimiter) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ja.length(); i += 1) {
if (i > 0) {
sb.append(',');
sb.append(delimiter);
}
Object object = ja.opt(i);
if (object != null) {
String string = object.toString();
if (string.length() > 0 && (string.indexOf(',') >= 0 ||
if (string.length() > 0 && (string.indexOf(delimiter) >= 0 ||
string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 ||
string.indexOf(0) >= 0 || string.charAt(0) == '"')) {
sb.append('"');
@@ -167,7 +208,19 @@ public class CDL {
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(String string) throws JSONException {
return toJSONArray(new JSONTokener(string));
return toJSONArray(string, ',');
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string,
* using the first row as a source of names.
* @param string The comma delimited text.
* @param delimiter custom delimiter char
* @return A JSONArray of JSONObjects.
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(String string, char delimiter) throws JSONException {
return toJSONArray(new JSONTokener(string), delimiter);
}
/**
@@ -178,7 +231,19 @@ public class CDL {
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(JSONTokener x) throws JSONException {
return toJSONArray(rowToJSONArray(x), x);
return toJSONArray(x, ',');
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string,
* using the first row as a source of names.
* @param x The JSONTokener containing the comma delimited text.
* @param delimiter custom delimiter char
* @return A JSONArray of JSONObjects.
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(JSONTokener x, char delimiter) throws JSONException {
return toJSONArray(rowToJSONArray(x, delimiter), x, delimiter);
}
/**
@@ -189,9 +254,21 @@ public class CDL {
* @return A JSONArray of JSONObjects.
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(JSONArray names, String string)
throws JSONException {
return toJSONArray(names, new JSONTokener(string));
public static JSONArray toJSONArray(JSONArray names, String string) throws JSONException {
return toJSONArray(names, string, ',');
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string
* using a supplied JSONArray as the source of element names.
* @param names A JSONArray of strings.
* @param string The comma delimited text.
* @param delimiter custom delimiter char
* @return A JSONArray of JSONObjects.
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(JSONArray names, String string, char delimiter) throws JSONException {
return toJSONArray(names, new JSONTokener(string), delimiter);
}
/**
@@ -202,14 +279,26 @@ public class CDL {
* @return A JSONArray of JSONObjects.
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(JSONArray names, JSONTokener x)
throws JSONException {
public static JSONArray toJSONArray(JSONArray names, JSONTokener x) throws JSONException {
return toJSONArray(names, x, ',');
}
/**
* Produce a JSONArray of JSONObjects from a comma delimited text string
* using a supplied JSONArray as the source of element names.
* @param names A JSONArray of strings.
* @param x A JSONTokener of the source text.
* @param delimiter custom delimiter char
* @return A JSONArray of JSONObjects.
* @throws JSONException if a called function fails
*/
public static JSONArray toJSONArray(JSONArray names, JSONTokener x, char delimiter) throws JSONException {
if (names == null || names.length() == 0) {
return null;
}
JSONArray ja = new JSONArray();
for (;;) {
JSONObject jo = rowToJSONObject(names, x);
JSONObject jo = rowToJSONObject(names, x, delimiter);
if (jo == null) {
break;
}
@@ -231,11 +320,24 @@ public class CDL {
* @throws JSONException if a called function fails
*/
public static String toString(JSONArray ja) throws JSONException {
return toString(ja, ',');
}
/**
* Produce a comma delimited text from a JSONArray of JSONObjects. The
* first row will be a list of names obtained by inspecting the first
* JSONObject.
* @param ja A JSONArray of JSONObjects.
* @param delimiter custom delimiter char
* @return A comma delimited text.
* @throws JSONException if a called function fails
*/
public static String toString(JSONArray ja, char delimiter) throws JSONException {
JSONObject jo = ja.optJSONObject(0);
if (jo != null) {
JSONArray names = jo.names();
if (names != null) {
return rowToString(names) + toString(names, ja);
return rowToString(names, delimiter) + toString(names, ja, delimiter);
}
}
return null;
@@ -250,8 +352,21 @@ public class CDL {
* @return A comma delimited text.
* @throws JSONException if a called function fails
*/
public static String toString(JSONArray names, JSONArray ja)
throws JSONException {
public static String toString(JSONArray names, JSONArray ja) throws JSONException {
return toString(names, ja, ',');
}
/**
* Produce a comma delimited text from a JSONArray of JSONObjects using
* a provided list of names. The list of names is not included in the
* output.
* @param names A JSONArray of strings.
* @param ja A JSONArray of JSONObjects.
* @param delimiter custom delimiter char
* @return A comma delimited text.
* @throws JSONException if a called function fails
*/
public static String toString(JSONArray names, JSONArray ja, char delimiter) throws JSONException {
if (names == null || names.length() == 0) {
return null;
}
@@ -259,7 +374,7 @@ public class CDL {
for (int i = 0; i < ja.length(); i += 1) {
JSONObject jo = ja.optJSONObject(i);
if (jo != null) {
sb.append(rowToString(jo.toJSONArray(names)));
sb.append(rowToString(jo.toJSONArray(names), delimiter));
}
}
return sb.toString();

View File

@@ -15,6 +15,12 @@ Public Domain.
*/
public class Cookie {
/**
* Constructs a new Cookie object.
*/
public Cookie() {
}
/**
* Produce a copy of a string in which the characters '+', '%', '=', ';'
* and control characters are replaced with "%hh". This is a gentle form

View File

@@ -11,6 +11,12 @@ Public Domain.
*/
public class CookieList {
/**
* Constructs a new CookieList object.
*/
public CookieList() {
}
/**
* Convert a cookie list into a JSONObject. A cookie list is a sequence
* of name/value pairs. The names are separated from the values by '='.
@@ -46,19 +52,19 @@ public class CookieList {
* @throws JSONException if a called function fails
*/
public static String toString(JSONObject jo) throws JSONException {
boolean b = false;
boolean isEndOfPair = false;
final StringBuilder sb = new StringBuilder();
// Don't use the new entrySet API to maintain Android support
for (final String key : jo.keySet()) {
final Object value = jo.opt(key);
if (!JSONObject.NULL.equals(value)) {
if (b) {
if (isEndOfPair) {
sb.append(';');
}
sb.append(Cookie.escape(key));
sb.append("=");
sb.append(Cookie.escape(value.toString()));
b = true;
isEndOfPair = true;
}
}
return sb.toString();

View File

@@ -13,6 +13,12 @@ import java.util.Locale;
*/
public class HTTP {
/**
* Constructs a new HTTP object.
*/
public HTTP() {
}
/** Carriage return/line feed. */
public static final String CRLF = "\r\n";

View File

@@ -149,11 +149,40 @@ public class JSONArray implements Iterable<Object> {
* A Collection.
*/
public JSONArray(Collection<?> collection) {
this(collection, 0, new JSONParserConfiguration());
}
/**
* Construct a JSONArray from a Collection.
*
* @param collection
* A Collection.
* @param jsonParserConfiguration
* Configuration object for the JSON parser
*/
public JSONArray(Collection<?> collection, JSONParserConfiguration jsonParserConfiguration) {
this(collection, 0, jsonParserConfiguration);
}
/**
* Construct a JSONArray from a collection with recursion depth.
*
* @param collection
* A Collection.
* @param recursionDepth
* Variable for tracking the count of nested object creations.
* @param jsonParserConfiguration
* Configuration object for the JSON parser
*/
JSONArray(Collection<?> collection, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONArray has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
}
if (collection == null) {
this.myArrayList = new ArrayList<Object>();
} else {
this.myArrayList = new ArrayList<Object>(collection.size());
this.addAll(collection, true);
this.addAll(collection, true, recursionDepth, jsonParserConfiguration);
}
}
@@ -205,7 +234,7 @@ public class JSONArray implements Iterable<Object> {
throw new JSONException(
"JSONArray initial value should be a string or collection or array.");
}
this.addAll(array, true);
this.addAll(array, true, 0);
}
/**
@@ -599,6 +628,38 @@ public class JSONArray implements Iterable<Object> {
}
}
/**
* Get the optional Boolean object associated with an index. It returns false
* if there is no value at that index, or if the value is not Boolean.TRUE
* or the String "true".
*
* @param index
* The index must be between 0 and length() - 1.
* @return The truth.
*/
public Boolean optBooleanObject(int index) {
return this.optBooleanObject(index, false);
}
/**
* Get the optional Boolean object associated with an index. It returns the
* defaultValue if there is no value at that index or if it is not a Boolean
* or the String "true" or "false" (case insensitive).
*
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* A boolean default.
* @return The truth.
*/
public Boolean optBooleanObject(int index, Boolean defaultValue) {
try {
return this.getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional double value associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
@@ -635,6 +696,42 @@ public class JSONArray implements Iterable<Object> {
return doubleValue;
}
/**
* Get the optional Double object associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The object.
*/
public Double optDoubleObject(int index) {
return this.optDoubleObject(index, Double.NaN);
}
/**
* Get the optional double value associated with an index. The defaultValue
* is returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index
* subscript
* @param defaultValue
* The default object.
* @return The object.
*/
public Double optDoubleObject(int index, Double defaultValue) {
final Number val = this.optNumber(index, null);
if (val == null) {
return defaultValue;
}
final Double doubleValue = val.doubleValue();
// if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
// return defaultValue;
// }
return doubleValue;
}
/**
* Get the optional float value associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
@@ -671,6 +768,42 @@ public class JSONArray implements Iterable<Object> {
return floatValue;
}
/**
* Get the optional Float object associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The object.
*/
public Float optFloatObject(int index) {
return this.optFloatObject(index, Float.NaN);
}
/**
* Get the optional Float object associated with an index. The defaultValue
* is returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index
* subscript
* @param defaultValue
* The default object.
* @return The object.
*/
public Float optFloatObject(int index, Float defaultValue) {
final Number val = this.optNumber(index, null);
if (val == null) {
return defaultValue;
}
final Float floatValue = val.floatValue();
// if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
// return floatValue;
// }
return floatValue;
}
/**
* Get the optional int value associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
@@ -703,6 +836,38 @@ public class JSONArray implements Iterable<Object> {
return val.intValue();
}
/**
* Get the optional Integer object associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The object.
*/
public Integer optIntegerObject(int index) {
return this.optIntegerObject(index, 0);
}
/**
* Get the optional Integer object associated with an index. The defaultValue is
* returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* The default object.
* @return The object.
*/
public Integer optIntegerObject(int index, Integer defaultValue) {
final Number val = this.optNumber(index, null);
if (val == null) {
return defaultValue;
}
return val.intValue();
}
/**
* Get the enum value associated with a key.
*
@@ -788,30 +953,57 @@ public class JSONArray implements Iterable<Object> {
}
/**
* Get the optional JSONArray associated with an index.
* Get the optional JSONArray associated with an index. Null is returned if
* there is no value at that index or if the value is not a JSONArray.
*
* @param index
* subscript
* @return A JSONArray value, or null if the index has no value, or if the
* value is not a JSONArray.
* The index must be between 0 and length() - 1.
* @return A JSONArray value.
*/
public JSONArray optJSONArray(int index) {
Object o = this.opt(index);
return o instanceof JSONArray ? (JSONArray) o : null;
return this.optJSONArray(index, null);
}
/**
* Get the optional JSONArray associated with an index. The defaultValue is returned if
* there is no value at that index or if the value is not a JSONArray.
*
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* The default.
* @return A JSONArray value.
*/
public JSONArray optJSONArray(int index, JSONArray defaultValue) {
Object object = this.opt(index);
return object instanceof JSONArray ? (JSONArray) object : defaultValue;
}
/**
* Get the optional JSONObject associated with an index. Null is returned if
* the key is not found, or null if the index has no value, or if the value
* is not a JSONObject.
* there is no value at that index or if the value is not a JSONObject.
*
* @param index
* The index must be between 0 and length() - 1.
* @return A JSONObject value.
*/
public JSONObject optJSONObject(int index) {
Object o = this.opt(index);
return o instanceof JSONObject ? (JSONObject) o : null;
return this.optJSONObject(index, null);
}
/**
* Get the optional JSONObject associated with an index. The defaultValue is returned if
* there is no value at that index or if the value is not a JSONObject.
*
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* The default.
* @return A JSONObject value.
*/
public JSONObject optJSONObject(int index, JSONObject defaultValue) {
Object object = this.opt(index);
return object instanceof JSONObject ? (JSONObject) object : defaultValue;
}
/**
@@ -846,6 +1038,38 @@ public class JSONArray implements Iterable<Object> {
return val.longValue();
}
/**
* Get the optional Long object associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index
* The index must be between 0 and length() - 1.
* @return The object.
*/
public Long optLongObject(int index) {
return this.optLongObject(index, 0L);
}
/**
* Get the optional Long object associated with an index. The defaultValue is
* returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index
* The index must be between 0 and length() - 1.
* @param defaultValue
* The default object.
* @return The object.
*/
public Long optLongObject(int index, Long defaultValue) {
final Number val = this.optNumber(index, null);
if (val == null) {
return defaultValue;
}
return val.longValue();
}
/**
* Get an optional {@link Number} value associated with a key, or <code>null</code>
* if there is no such key or if the value is not a number. If the value is a string,
@@ -1135,7 +1359,8 @@ public class JSONArray implements Iterable<Object> {
* The subscript.
* @param value
* The Map value.
* @return this.
* @return
* reference to self
* @throws JSONException
* If the index is negative or if the value is an invalid
* number.
@@ -1143,7 +1368,27 @@ public class JSONArray implements Iterable<Object> {
* If a key in the map is <code>null</code>
*/
public JSONArray put(int index, Map<?, ?> value) throws JSONException {
this.put(index, new JSONObject(value));
this.put(index, new JSONObject(value, new JSONParserConfiguration()));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject that
* is produced from a Map.
*
* @param index
* The subscript
* @param value
* The Map value.
* @param jsonParserConfiguration
* Configuration object for the JSON parser
* @return reference to self
* @throws JSONException
* If the index is negative or if the value is an invalid
* number.
*/
public JSONArray put(int index, Map<?, ?> value, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this.put(index, new JSONObject(value, jsonParserConfiguration));
return this;
}
@@ -1451,10 +1696,8 @@ public class JSONArray implements Iterable<Object> {
@SuppressWarnings("resource")
public String toString(int indentFactor) throws JSONException {
StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
return this.write(sw, indentFactor, 0).toString();
}
}
/**
* Write the contents of the JSONArray as JSON text to a writer. For
@@ -1586,13 +1829,14 @@ public class JSONArray implements Iterable<Object> {
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
*
* @param recursionDepth
* Variable for tracking the count of nested object creations.
*/
private void addAll(Collection<?> collection, boolean wrap) {
private void addAll(Collection<?> collection, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size());
if (wrap) {
for (Object o: collection){
this.put(JSONObject.wrap(o));
this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration));
}
} else {
for (Object o: collection){
@@ -1632,19 +1876,55 @@ public class JSONArray implements Iterable<Object> {
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
* @throws JSONException
* If not an array or if an array value is non-finite number.
*/
private void addAll(Object array, boolean wrap) throws JSONException {
this.addAll(array, wrap, 0);
}
/**
* Add an array's elements to the JSONArray.
*
* @param array
* Array. If the parameter passed is null, or not an array,
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
* @param recursionDepth
* Variable for tracking the count of nested object creations.
*/
private void addAll(Object array, boolean wrap, int recursionDepth) {
addAll(array, wrap, recursionDepth, new JSONParserConfiguration());
}
/**
* Add an array's elements to the JSONArray.
*`
* @param array
* Array. If the parameter passed is null, or not an array,
* JSONArray, Collection, or Iterable, an exception will be
* thrown.
* @param wrap
* {@code true} to call {@link JSONObject#wrap(Object)} for each item,
* {@code false} to add the items directly
* @param recursionDepth
* Variable for tracking the count of nested object creations.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
* @throws JSONException
* If not an array or if an array value is non-finite number.
* @throws NullPointerException
* Thrown if the array parameter is null.
*/
private void addAll(Object array, boolean wrap) throws JSONException {
private void addAll(Object array, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
if (array.getClass().isArray()) {
int length = Array.getLength(array);
this.myArrayList.ensureCapacity(this.myArrayList.size() + length);
if (wrap) {
for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i)));
this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1, jsonParserConfiguration));
}
} else {
for (int i = 0; i < length; i += 1) {
@@ -1657,7 +1937,7 @@ public class JSONArray implements Iterable<Object> {
// JSONArray
this.myArrayList.addAll(((JSONArray)array).myArrayList);
} else if (array instanceof Collection) {
this.addAll((Collection<?>)array, wrap);
this.addAll((Collection<?>)array, wrap, recursionDepth);
} else if (array instanceof Iterable) {
this.addAll((Iterable<?>)array, wrap);
} else {

View File

@@ -13,6 +13,13 @@ Public Domain.
* @version 2016-01-30
*/
public class JSONML {
/**
* Constructs a new JSONML object.
*/
public JSONML() {
}
/**
* Parse XML values and store them in a JSONArray.
* @param x The XMLTokener containing the source string.
@@ -27,7 +34,32 @@ public class JSONML {
XMLTokener x,
boolean arrayForm,
JSONArray ja,
boolean keepStrings
boolean keepStrings,
int currentNestingDepth
) throws JSONException {
return parse(x,arrayForm, ja,
keepStrings ? JSONMLParserConfiguration.KEEP_STRINGS : JSONMLParserConfiguration.ORIGINAL,
currentNestingDepth);
}
/**
* Parse XML values and store them in a JSONArray.
* @param x The XMLTokener containing the source string.
* @param arrayForm true if array form, false if object form.
* @param ja The JSONArray that is containing the current tag or null
* if we are at the outermost level.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means Don't type-convert text nodes and attribute values.
* @return A JSONArray if the value is the outermost tag, otherwise null.
* @throws JSONException if a parsing error occurs
*/
private static Object parse(
XMLTokener x,
boolean arrayForm,
JSONArray ja,
JSONMLParserConfiguration config,
int currentNestingDepth
) throws JSONException {
String attribute;
char c;
@@ -152,7 +184,7 @@ public class JSONML {
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
}
newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token));
newjo.accumulate(attribute, config.isKeepStrings() ? ((String)token) :XML.stringToValue((String)token));
token = null;
} else {
newjo.accumulate(attribute, "");
@@ -181,7 +213,12 @@ public class JSONML {
if (token != XML.GT) {
throw x.syntaxError("Misshaped tag");
}
closeTag = (String)parse(x, arrayForm, newja, keepStrings);
if (currentNestingDepth == config.getMaxNestingDepth()) {
throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached");
}
closeTag = (String)parse(x, arrayForm, newja, config, currentNestingDepth + 1);
if (closeTag != null) {
if (!closeTag.equals(tagName)) {
throw x.syntaxError("Mismatched '" + tagName +
@@ -203,7 +240,7 @@ public class JSONML {
} else {
if (ja != null) {
ja.put(token instanceof String
? keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token)
? (config.isKeepStrings() ? XML.unescape((String)token) : XML.stringToValue((String)token))
: token);
}
}
@@ -224,7 +261,7 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(String string) throws JSONException {
return (JSONArray)parse(new XMLTokener(string), true, null, false);
return (JSONArray)parse(new XMLTokener(string), true, null, JSONMLParserConfiguration.ORIGINAL, 0);
}
@@ -246,7 +283,56 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException {
return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings);
return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings, 0);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child tags.
* As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type
* but just leaves it as a string.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param string The source string.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(String string, JSONMLParserConfiguration config) throws JSONException {
return (JSONArray)parse(new XMLTokener(string), true, null, config, 0);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child content and tags.
* As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type
* but just leaves it as a string.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param x An XMLTokener.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(XMLTokener x, JSONMLParserConfiguration config) throws JSONException {
return (JSONArray)parse(x, true, null, config, 0);
}
@@ -268,7 +354,7 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException {
return (JSONArray)parse(x, true, null, keepStrings);
return (JSONArray)parse(x, true, null, keepStrings, 0);
}
@@ -285,7 +371,7 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
return (JSONArray)parse(x, true, null, false);
return (JSONArray)parse(x, true, null, false, 0);
}
@@ -303,7 +389,7 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(String string) throws JSONException {
return (JSONObject)parse(new XMLTokener(string), false, null, false);
return (JSONObject)parse(new XMLTokener(string), false, null, false, 0);
}
@@ -323,7 +409,29 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings);
return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings, 0);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param string The XML source text.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(String string, JSONMLParserConfiguration config) throws JSONException {
return (JSONObject)parse(new XMLTokener(string), false, null, config, 0);
}
@@ -341,7 +449,7 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
return (JSONObject)parse(x, false, null, false);
return (JSONObject)parse(x, false, null, false, 0);
}
@@ -361,7 +469,29 @@ public class JSONML {
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException {
return (JSONObject)parse(x, false, null, keepStrings);
return (JSONObject)parse(x, false, null, keepStrings, 0);
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.
* Comments, prologs, DTDs, and <pre>{@code &lt;[ [ ]]>}</pre> are ignored.
* @param x An XMLTokener of the XML source text.
* @param config The parser configuration:
* JSONMLParserConfiguration.ORIGINAL is the default behaviour;
* JSONMLParserConfiguration.KEEP_STRINGS means values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(XMLTokener x, JSONMLParserConfiguration config) throws JSONException {
return (JSONObject)parse(x, false, null, config, 0);
}
@@ -442,6 +572,7 @@ public class JSONML {
return sb.toString();
}
/**
* Reverse the JSONML transformation, making an XML text from a JSONObject.
* The JSONObject must contain a "tagName" property. If it has children,

View File

@@ -0,0 +1,69 @@
package org.json;
/*
Public Domain.
*/
/**
* Configuration object for the XML to JSONML parser. The configuration is immutable.
*/
@SuppressWarnings({""})
public class JSONMLParserConfiguration extends ParserConfiguration {
/**
* We can override the default maximum nesting depth if needed.
*/
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH;
/** Original Configuration of the XML to JSONML Parser. */
public static final JSONMLParserConfiguration ORIGINAL
= new JSONMLParserConfiguration();
/** Original configuration of the XML to JSONML Parser except that values are kept as strings. */
public static final JSONMLParserConfiguration KEEP_STRINGS
= new JSONMLParserConfiguration().withKeepStrings(true);
/**
* Default parser configuration. Does not keep strings (tries to implicitly convert values).
*/
public JSONMLParserConfiguration() {
super();
this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
}
/**
* Configure the parser string processing and use the default CDATA Tag Name as "content".
* @param keepStrings <code>true</code> to parse all values as string.
* <code>false</code> to try and convert XML string values into a JSON value.
* @param maxNestingDepth <code>int</code> to limit the nesting depth
*/
protected JSONMLParserConfiguration(final boolean keepStrings, final int maxNestingDepth) {
super(keepStrings, maxNestingDepth);
}
/**
* Provides a new instance of the same configuration.
*/
@Override
protected JSONMLParserConfiguration clone() {
// future modifications to this method should always ensure a "deep"
// clone in the case of collections. i.e. if a Map is added as a configuration
// item, a new map instance should be created and if possible each value in the
// map should be cloned as well. If the values of the map are known to also
// be immutable, then a shallow clone of the map is acceptable.
return new JSONMLParserConfiguration(
this.keepStrings,
this.maxNestingDepth
);
}
@SuppressWarnings("unchecked")
@Override
public JSONMLParserConfiguration withKeepStrings(final boolean newVal) {
return super.withKeepStrings(newVal);
}
@SuppressWarnings("unchecked")
@Override
public JSONMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);
}
}

View File

@@ -145,6 +145,11 @@ public class JSONObject {
*/
private final Map<String, Object> map;
/**
* Retrieves the type of the underlying Map in this class.
*
* @return The class object representing the type of the underlying Map.
*/
public Class<? extends Map> getMapType() {
return map.getClass();
}
@@ -208,22 +213,14 @@ public class JSONObject {
throw x.syntaxError("A JSONObject text must begin with '{'");
}
for (;;) {
char prev = x.getPrevious();
c = x.nextClean();
switch (c) {
case 0:
throw x.syntaxError("A JSONObject text must end with '}'");
case '}':
return;
case '{':
case '[':
if(prev=='{') {
throw x.syntaxError("A JSON Object can not directly nest another JSON Object or JSON Array.");
}
// fall through
default:
x.back();
key = x.nextValue().toString();
key = x.nextSimpleValue(c).toString();
}
// The key is followed by ':'.
@@ -256,6 +253,9 @@ public class JSONObject {
if (x.nextClean() == '}') {
return;
}
if (x.end()) {
throw x.syntaxError("A JSONObject text must end with '}'");
}
x.back();
break;
case '}':
@@ -278,6 +278,30 @@ public class JSONObject {
* If a key in the map is <code>null</code>
*/
public JSONObject(Map<?, ?> m) {
this(m, 0, new JSONParserConfiguration());
}
/**
* Construct a JSONObject from a Map with custom json parse configurations.
*
* @param m
* A map object that can be used to initialize the contents of
* the JSONObject.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
*/
public JSONObject(Map<?, ?> m, JSONParserConfiguration jsonParserConfiguration) {
this(m, 0, jsonParserConfiguration);
}
/**
* Construct a JSONObject from a map with recursion depth.
*
*/
private JSONObject(Map<?, ?> m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) {
throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth());
}
if (m == null) {
this.map = new HashMap<String, Object>();
} else {
@@ -288,7 +312,8 @@ public class JSONObject {
}
final Object value = e.getValue();
if (value != null) {
this.map.put(String.valueOf(e.getKey()), wrap(value));
testValidity(value);
this.map.put(String.valueOf(e.getKey()), wrap(value, recursionDepth + 1, jsonParserConfiguration));
}
}
}
@@ -346,11 +371,12 @@ public class JSONObject {
* &#64;JSONPropertyIgnore
* public String getName() { return this.name; }
* </pre>
* <p>
*
* @param bean
* An object that has getter methods that should be used to make
* a JSONObject.
* @throws JSONException
* If a getter returned a non-finite number.
*/
public JSONObject(Object bean) {
this();
@@ -1131,6 +1157,45 @@ public class JSONObject {
}
}
/**
* Get an optional boolean object associated with a key. It returns false if there
* is no such key, or if the value is not Boolean.TRUE or the String "true".
*
* @param key
* A key string.
* @return The truth.
*/
public Boolean optBooleanObject(String key) {
return this.optBooleanObject(key, false);
}
/**
* Get an optional boolean object associated with a key. It returns the
* defaultValue if there is no such key, or if it is not a Boolean or the
* String "true" or "false" (case insensitive).
*
* @param key
* A key string.
* @param defaultValue
* The default.
* @return The truth.
*/
public Boolean optBooleanObject(String key, Boolean defaultValue) {
Object val = this.opt(key);
if (NULL.equals(val)) {
return defaultValue;
}
if (val instanceof Boolean){
return ((Boolean) val).booleanValue();
}
try {
// we'll use the get anyway because it does string conversion.
return this.getBoolean(key);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get an optional BigDecimal associated with a key, or the defaultValue if
* there is no such key or if its value is not a number. If the value is a
@@ -1290,15 +1355,43 @@ public class JSONObject {
if (val == null) {
return defaultValue;
}
final double doubleValue = val.doubleValue();
// if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
// return defaultValue;
// }
return doubleValue;
return val.doubleValue();
}
/**
* Get the optional double value associated with an index. NaN is returned
* Get an optional Double object associated with a key, or NaN if there is no such
* key or if its value is not a number. If the value is a string, an attempt
* will be made to evaluate it as a number.
*
* @param key
* A string which is the key.
* @return An object which is the value.
*/
public Double optDoubleObject(String key) {
return this.optDoubleObject(key, Double.NaN);
}
/**
* Get an optional Double object associated with a key, or the defaultValue if
* there is no such key or if its value is not a number. If the value is a
* string, an attempt will be made to evaluate it as a number.
*
* @param key
* A key string.
* @param defaultValue
* The default.
* @return An object which is the value.
*/
public Double optDoubleObject(String key, Double defaultValue) {
Number val = this.optNumber(key);
if (val == null) {
return defaultValue;
}
return val.doubleValue();
}
/**
* Get the optional float value associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
@@ -1311,7 +1404,7 @@ public class JSONObject {
}
/**
* Get the optional double value associated with an index. The defaultValue
* Get the optional float value associated with an index. The defaultValue
* is returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
@@ -1333,6 +1426,42 @@ public class JSONObject {
return floatValue;
}
/**
* Get the optional Float object associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param key
* A key string.
* @return The object.
*/
public Float optFloatObject(String key) {
return this.optFloatObject(key, Float.NaN);
}
/**
* Get the optional Float object associated with an index. The defaultValue
* is returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param key
* A key string.
* @param defaultValue
* The default object.
* @return The object.
*/
public Float optFloatObject(String key, Float defaultValue) {
Number val = this.optNumber(key);
if (val == null) {
return defaultValue;
}
final Float floatValue = val.floatValue();
// if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
// return defaultValue;
// }
return floatValue;
}
/**
* Get an optional int value associated with a key, or zero if there is no
* such key or if the value is not a number. If the value is a string, an
@@ -1365,6 +1494,38 @@ public class JSONObject {
return val.intValue();
}
/**
* Get an optional Integer object associated with a key, or zero if there is no
* such key or if the value is not a number. If the value is a string, an
* attempt will be made to evaluate it as a number.
*
* @param key
* A key string.
* @return An object which is the value.
*/
public Integer optIntegerObject(String key) {
return this.optIntegerObject(key, 0);
}
/**
* Get an optional Integer object associated with a key, or the default if there
* is no such key or if the value is not a number. If the value is a string,
* an attempt will be made to evaluate it as a number.
*
* @param key
* A key string.
* @param defaultValue
* The default.
* @return An object which is the value.
*/
public Integer optIntegerObject(String key, Integer defaultValue) {
final Number val = this.optNumber(key, null);
if (val == null) {
return defaultValue;
}
return val.intValue();
}
/**
* Get an optional JSONArray associated with a key. It returns null if there
* is no such key, or if its value is not a JSONArray.
@@ -1374,8 +1535,22 @@ public class JSONObject {
* @return A JSONArray which is the value.
*/
public JSONArray optJSONArray(String key) {
Object o = this.opt(key);
return o instanceof JSONArray ? (JSONArray) o : null;
return this.optJSONArray(key, null);
}
/**
* Get an optional JSONArray associated with a key, or the default if there
* is no such key, or if its value is not a JSONArray.
*
* @param key
* A key string.
* @param defaultValue
* The default.
* @return A JSONArray which is the value.
*/
public JSONArray optJSONArray(String key, JSONArray defaultValue) {
Object object = this.opt(key);
return object instanceof JSONArray ? (JSONArray) object : defaultValue;
}
/**
@@ -1436,6 +1611,39 @@ public class JSONObject {
return val.longValue();
}
/**
* Get an optional Long object associated with a key, or zero if there is no
* such key or if the value is not a number. If the value is a string, an
* attempt will be made to evaluate it as a number.
*
* @param key
* A key string.
* @return An object which is the value.
*/
public Long optLongObject(String key) {
return this.optLongObject(key, 0L);
}
/**
* Get an optional Long object associated with a key, or the default if there
* is no such key or if the value is not a number. If the value is a string,
* an attempt will be made to evaluate it as a number.
*
* @param key
* A key string.
* @param defaultValue
* The default.
* @return An object which is the value.
*/
public Long optLongObject(String key, Long defaultValue) {
final Number val = this.optNumber(key, null);
if (val == null) {
return defaultValue;
}
return val.longValue();
}
/**
* Get an optional {@link Number} value associated with a key, or <code>null</code>
* if there is no such key or if the value is not a number. If the value is a string,
@@ -1514,6 +1722,8 @@ public class JSONObject {
*
* @param bean
* the bean
* @throws JSONException
* If a getter returned a non-finite number.
*/
private void populateMap(Object bean) {
populateMap(bean, Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()));
@@ -1549,6 +1759,7 @@ public class JSONObject {
objectsRecord.add(result);
testValidity(result);
this.map.put(key, wrap(result, objectsRecord));
objectsRecord.remove(result);
@@ -1655,6 +1866,10 @@ public class JSONObject {
}
}
//If the superclass is Object, no annotations will be found any more
if (c.getSuperclass().equals(Object.class))
return null;
try {
return getAnnotation(
c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()),
@@ -1709,6 +1924,10 @@ public class JSONObject {
}
}
//If the superclass is Object, no annotations will be found any more
if (c.getSuperclass().equals(Object.class))
return -1;
try {
int d = getAnnotationDepth(
c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()),
@@ -2006,7 +2225,6 @@ public class JSONObject {
@SuppressWarnings("resource")
public static String quote(String string) {
StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
try {
return quote(string, sw).toString();
} catch (IOException ignored) {
@@ -2014,8 +2232,15 @@ public class JSONObject {
return "";
}
}
}
/**
* Quotes a string and appends the result to a given Writer.
*
* @param string The input string to be quoted.
* @param w The Writer to which the quoted string will be appended.
* @return The same Writer instance after appending the quoted string.
* @throws IOException If an I/O error occurs while writing to the Writer.
*/
public static Writer quote(String string, Writer w) throws IOException {
if (string == null || string.isEmpty()) {
w.write("\"\"");
@@ -2199,6 +2424,49 @@ public class JSONObject {
|| val.indexOf('E') > -1 || "-0".equals(val);
}
/**
* Try to convert a string into a number, boolean, or null. If the string
* can't be converted, return the string.
*
* @param string
* A String. can not be null.
* @return A simple JSON value.
* @throws NullPointerException
* Thrown if the string is null.
*/
// Changes to this method must be copied to the corresponding method in
// the XML class to keep full support for Android
public static Object stringToValue(String string) {
if ("".equals(string)) {
return string;
}
// check JSON key words true/false/null
if ("true".equalsIgnoreCase(string)) {
return Boolean.TRUE;
}
if ("false".equalsIgnoreCase(string)) {
return Boolean.FALSE;
}
if ("null".equalsIgnoreCase(string)) {
return JSONObject.NULL;
}
/*
* If it might be a number, try converting it. If a number cannot be
* produced, then the value will just be a string.
*/
char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
try {
return stringToNumber(string);
} catch (Exception ignore) {
}
}
return string;
}
/**
* Converts a string to a number using the narrowest possible type. Possible
* returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
@@ -2269,49 +2537,6 @@ public class JSONObject {
throw new NumberFormatException("val ["+val+"] is not a valid number.");
}
/**
* Try to convert a string into a number, boolean, or null. If the string
* can't be converted, return the string.
*
* @param string
* A String. can not be null.
* @return A simple JSON value.
* @throws NullPointerException
* Thrown if the string is null.
*/
// Changes to this method must be copied to the corresponding method in
// the XML class to keep full support for Android
public static Object stringToValue(String string) {
if ("".equals(string)) {
return string;
}
// check JSON key words true/false/null
if ("true".equalsIgnoreCase(string)) {
return Boolean.TRUE;
}
if ("false".equalsIgnoreCase(string)) {
return Boolean.FALSE;
}
if ("null".equalsIgnoreCase(string)) {
return JSONObject.NULL;
}
/*
* If it might be a number, try converting it. If a number cannot be
* produced, then the value will just be a string.
*/
char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
try {
return stringToNumber(string);
} catch (Exception ignore) {
}
}
return string;
}
/**
* Throw an exception if the object is a NaN or infinite number.
*
@@ -2399,10 +2624,8 @@ public class JSONObject {
@SuppressWarnings("resource")
public String toString(int indentFactor) throws JSONException {
StringWriter w = new StringWriter();
synchronized (w.getBuffer()) {
return this.write(w, indentFactor, 0).toString();
}
}
/**
* Make a JSON text of an Object value. If the object has an
@@ -2452,7 +2675,31 @@ public class JSONObject {
return wrap(object, null);
}
/**
* Wrap an object, if necessary. If the object is <code>null</code>, return the NULL
* object. If it is an array or collection, wrap it in a JSONArray. If it is
* a map, wrap it in a JSONObject. If it is a standard property (Double,
* String, et al) then it is already wrapped. Otherwise, if it comes from
* one of the java packages, turn it into a string. And if it doesn't, try
* to wrap it in a JSONObject. If the wrapping fails, then null is returned.
*
* @param object
* The object to wrap
* @param recursionDepth
* Variable for tracking the count of nested object creations.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
* @return The wrapped value
*/
static Object wrap(Object object, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
return wrap(object, null, recursionDepth, jsonParserConfiguration);
}
private static Object wrap(Object object, Set<Object> objectsRecord) {
return wrap(object, objectsRecord, 0, new JSONParserConfiguration());
}
private static Object wrap(Object object, Set<Object> objectsRecord, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) {
try {
if (NULL.equals(object)) {
return NULL;
@@ -2470,14 +2717,14 @@ public class JSONObject {
if (object instanceof Collection) {
Collection<?> coll = (Collection<?>) object;
return new JSONArray(coll);
return new JSONArray(coll, recursionDepth, jsonParserConfiguration);
}
if (object.getClass().isArray()) {
return new JSONArray(object);
}
if (object instanceof Map) {
Map<?, ?> map = (Map<?, ?>) object;
return new JSONObject(map);
return new JSONObject(map, recursionDepth, jsonParserConfiguration);
}
Package objectPackage = object.getClass().getPackage();
String objectPackageName = objectPackage != null ? objectPackage
@@ -2713,4 +2960,24 @@ public class JSONObject {
"JavaBean object contains recursively defined member variable of key " + quote(key)
);
}
/**
* For a prospective number, remove the leading zeros
* @param value prospective number
* @return number without leading zeros
*/
private static String removeLeadingZerosOfNumber(String value){
if (value.equals("-")){return value;}
boolean negativeFirstChar = (value.charAt(0) == '-');
int counter = negativeFirstChar ? 1:0;
while (counter < value.length()){
if (value.charAt(counter) != '0'){
if (negativeFirstChar) {return "-".concat(value.substring(counter));}
return value.substring(counter);
}
++counter;
}
if (negativeFirstChar) {return "-0";}
return "0";
}
}

View File

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

View File

@@ -42,6 +42,12 @@ public class JSONPointer {
*/
public static class Builder {
/**
* Constructs a new Builder object.
*/
public Builder() {
}
// Segments for the eventual JSONPointer string
private final List<String> refTokens = new ArrayList<String>();
@@ -163,6 +169,12 @@ public class JSONPointer {
//}
}
/**
* Constructs a new JSONPointer instance with the provided list of reference tokens.
*
* @param refTokens A list of strings representing the reference tokens for the JSON Pointer.
* Each token identifies a step in the path to the targeted value.
*/
public JSONPointer(List<String> refTokens) {
this.refTokens = new ArrayList<String>(refTokens);
}

View File

@@ -14,10 +14,21 @@ Public Domain.
public class JSONPointerException extends JSONException {
private static final long serialVersionUID = 8872944667561856751L;
/**
* Constructs a new JSONPointerException with the specified error message.
*
* @param message The detail message describing the reason for the exception.
*/
public JSONPointerException(String message) {
super(message);
}
/**
* Constructs a new JSONPointerException with the specified error message and cause.
*
* @param message The detail message describing the reason for the exception.
* @param cause The cause of the exception.
*/
public JSONPointerException(String message, Throwable cause) {
super(message, cause);
}

View File

@@ -11,13 +11,13 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/**
* Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. If this annotation is
* present at any level in the class hierarchy, then the method will
* not be serialized from the bean into the JSONObject.
*/
@Documented
@Retention(RUNTIME)
@Target({METHOD})
public @interface JSONPropertyIgnore { }

View File

@@ -11,16 +11,17 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/**
* Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. A value set to empty string <code>""</code>
* will have the Bean parser fall back to the default field name processing.
*/
@Documented
@Retention(RUNTIME)
@Target({METHOD})
public @interface JSONPropertyName {
/**
* The value of the JSON property.
* @return The name of the property as to be used in the JSON Object.
*/
String value();

View File

@@ -1,11 +1,7 @@
package org.json;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.*;
import java.nio.charset.Charset;
/*
Public Domain.
@@ -61,7 +57,7 @@ public class JSONTokener {
* @param inputStream The source.
*/
public JSONTokener(InputStream inputStream) {
this(new InputStreamReader(inputStream));
this(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
}
@@ -406,12 +402,7 @@ public class JSONTokener {
*/
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c);
case '{':
this.back();
try {
@@ -427,6 +418,17 @@ public class JSONTokener {
throw new JSONException("JSON Array or Object depth too large to process.", e);
}
}
return nextSimpleValue(c);
}
Object nextSimpleValue(char c) {
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c);
}
/*
* Handle unquoted text. This could be the values true, false, or
@@ -522,4 +524,15 @@ public class JSONTokener {
return " at " + this.index + " [character " + this.character + " line " +
this.line + "]";
}
/**
* Closes the underlying reader, releasing any resources associated with it.
*
* @throws IOException If an I/O error occurs while closing the reader.
*/
public void close() throws IOException {
if(reader!=null){
reader.close();
}
}
}

View File

@@ -0,0 +1,126 @@
package org.json;
/*
Public Domain.
*/
/**
* Configuration base object for parsers. The configuration is immutable.
*/
@SuppressWarnings({""})
public class ParserConfiguration {
/**
* Used to indicate there's no defined limit to the maximum nesting depth when parsing a document.
*/
public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1;
/**
* The default maximum nesting depth when parsing a document.
*/
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512;
/**
* Specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*/
protected boolean keepStrings;
/**
* The maximum nesting depth when parsing a document.
*/
protected int maxNestingDepth;
/**
* Constructs a new ParserConfiguration with default settings.
*/
public ParserConfiguration() {
this.keepStrings = false;
this.maxNestingDepth = DEFAULT_MAXIMUM_NESTING_DEPTH;
}
/**
* Constructs a new ParserConfiguration with the specified settings.
*
* @param keepStrings A boolean indicating whether to preserve strings during parsing.
* @param maxNestingDepth An integer representing the maximum allowed nesting depth.
*/
protected ParserConfiguration(final boolean keepStrings, final int maxNestingDepth) {
this.keepStrings = keepStrings;
this.maxNestingDepth = maxNestingDepth;
}
/**
* Provides a new instance of the same configuration.
*/
@Override
protected ParserConfiguration clone() {
// future modifications to this method should always ensure a "deep"
// clone in the case of collections. i.e. if a Map is added as a configuration
// item, a new map instance should be created and if possible each value in the
// map should be cloned as well. If the values of the map are known to also
// be immutable, then a shallow clone of the map is acceptable.
return new ParserConfiguration(
this.keepStrings,
this.maxNestingDepth
);
}
/**
* When parsing the XML into JSONML, specifies if values 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 isKeepStrings() {
return this.keepStrings;
}
/**
* When parsing the XML into JSONML, specifies if values 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>keepStrings</code> configuration option.
* @param <T> the type of the configuration object
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
public <T extends ParserConfiguration> T withKeepStrings(final boolean newVal) {
T newConfig = (T)this.clone();
newConfig.keepStrings = newVal;
return newConfig;
}
/**
* The maximum nesting depth that the parser will descend before throwing an exception
* when parsing the XML into JSONML.
* @return the maximum nesting depth set for this configuration
*/
public int getMaxNestingDepth() {
return maxNestingDepth;
}
/**
* Defines the maximum nesting depth that the parser will descend before throwing an exception
* when parsing the XML into JSONML. The default max nesting depth is 512, which means the parser
* will throw a JsonException if the maximum depth is reached.
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
* which means the parses will go as deep as the maximum call stack size allows.
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @param <T> the type of the configuration object
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
public <T extends ParserConfiguration> T withMaxNestingDepth(int maxNestingDepth) {
T newConfig = (T)this.clone();
if (maxNestingDepth > UNDEFINED_MAXIMUM_NESTING_DEPTH) {
newConfig.maxNestingDepth = maxNestingDepth;
} else {
newConfig.maxNestingDepth = UNDEFINED_MAXIMUM_NESTING_DEPTH;
}
return newConfig;
}
}

View File

@@ -13,6 +13,13 @@ import java.util.Properties;
* @version 2015-05-05
*/
public class Property {
/**
* Constructs a new Property object.
*/
public Property() {
}
/**
* Converts a property file object into a JSONObject. The property file object is a table of name value pairs.
* @param properties java.util.Properties

View File

@@ -10,7 +10,6 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
/**
* This provides static methods to convert an XML text into a JSONObject, and to
* covert a JSONObject into an XML text.
@@ -21,6 +20,12 @@ import java.util.Iterator;
@SuppressWarnings("boxing")
public class XML {
/**
* Constructs a new XML object.
*/
public XML() {
}
/** The Character '&amp;'. */
public static final Character AMP = '&';
@@ -53,6 +58,9 @@ public class XML {
*/
public static final String NULL_ATTR = "xsi:nil";
/**
* Represents the XML attribute name for specifying type information.
*/
public static final String TYPE_ATTR = "xsi:type";
/**
@@ -229,10 +237,14 @@ public class XML {
* The JSONObject that will include the new material.
* @param name
* The tag name.
* @param config
* The XML parser configuration.
* @param currentNestingDepth
* The current nesting depth.
* @return true if the close tag is processed.
* @throws JSONException
* @throws JSONException Thrown if any parsing error occurs.
*/
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config)
private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, int currentNestingDepth)
throws JSONException {
char c;
int i;
@@ -402,7 +414,11 @@ public class XML {
} else if (token == LT) {
// Nested element
if (parse(x, jsonObject, tagName, config)) {
if (currentNestingDepth == config.getMaxNestingDepth()) {
throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached");
}
if (parse(x, jsonObject, tagName, config, currentNestingDepth + 1)) {
if (config.getForceList().contains(tagName)) {
// Force the value to be an array
if (jsonObject.length() == 0) {
@@ -420,6 +436,9 @@ public class XML {
&& jsonObject.opt(config.getcDataTagName()) != null) {
context.accumulate(tagName, jsonObject.opt(config.getcDataTagName()));
} else {
if (!config.shouldTrimWhiteSpace()) {
removeEmpty(jsonObject, config);
}
context.accumulate(tagName, jsonObject);
}
}
@@ -434,58 +453,46 @@ public class XML {
}
}
}
/**
* This method tries to convert the given string value to the target object
* @param string String to convert
* @param typeConverter value converter to convert string to integer, boolean e.t.c
* @return JSON value of this string or the string
* This method removes any JSON entry which has the key set by XMLParserConfiguration.cDataTagName
* and contains whitespace as this is caused by whitespace between tags. See test XMLTest.testNestedWithWhitespaceTrimmingDisabled.
* @param jsonObject JSONObject which may require deletion
* @param config The XMLParserConfiguration which includes the cDataTagName
*/
public static Object stringToValue(String string, XMLXsiTypeConverter<?> typeConverter) {
if(typeConverter != null) {
return typeConverter.convert(string);
private static void removeEmpty(final JSONObject jsonObject, final XMLParserConfiguration config) {
if (jsonObject.has(config.getcDataTagName())) {
final Object s = jsonObject.get(config.getcDataTagName());
if (s instanceof String) {
if (isStringAllWhiteSpace(s.toString())) {
jsonObject.remove(config.getcDataTagName());
}
}
else if (s instanceof JSONArray) {
final JSONArray sArray = (JSONArray) s;
for (int k = sArray.length()-1; k >= 0; k--){
final Object eachString = sArray.get(k);
if (eachString instanceof String) {
String s1 = (String) eachString;
if (isStringAllWhiteSpace(s1)) {
sArray.remove(k);
}
}
}
if (sArray.isEmpty()) {
jsonObject.remove(config.getcDataTagName());
}
}
}
return stringToValue(string);
}
/**
* This method is the same as {@link JSONObject#stringToValue(String)}.
*
* @param string String to convert
* @return JSON value of this string or the string
*/
// To maintain compatibility with the Android API, this method is a direct copy of
// the one in JSONObject. Changes made here should be reflected there.
// This method should not make calls out of the XML object.
public static Object stringToValue(String string) {
if ("".equals(string)) {
return string;
}
// check JSON key words true/false/null
if ("true".equalsIgnoreCase(string)) {
return Boolean.TRUE;
}
if ("false".equalsIgnoreCase(string)) {
return Boolean.FALSE;
}
if ("null".equalsIgnoreCase(string)) {
return JSONObject.NULL;
}
/*
* If it might be a number, try converting it. If a number cannot be
* produced, then the value will just be a string.
*/
char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
try {
return stringToNumber(string);
} catch (Exception ignore) {
private static boolean isStringAllWhiteSpace(final String s) {
for (int k = 0; k<s.length(); k++){
final char eachChar = s.charAt(k);
if (!Character.isWhitespace(eachChar)) {
return false;
}
}
return string;
return true;
}
/**
@@ -559,6 +566,58 @@ public class XML {
|| val.indexOf('E') > -1 || "-0".equals(val);
}
/**
* This method tries to convert the given string value to the target object
* @param string String to convert
* @param typeConverter value converter to convert string to integer, boolean e.t.c
* @return JSON value of this string or the string
*/
public static Object stringToValue(String string, XMLXsiTypeConverter<?> typeConverter) {
if(typeConverter != null) {
return typeConverter.convert(string);
}
return stringToValue(string);
}
/**
* This method is the same as {@link JSONObject#stringToValue(String)}.
*
* @param string String to convert
* @return JSON value of this string or the string
*/
// To maintain compatibility with the Android API, this method is a direct copy of
// the one in JSONObject. Changes made here should be reflected there.
// This method should not make calls out of the XML object.
public static Object stringToValue(String string) {
if ("".equals(string)) {
return string;
}
// check JSON key words true/false/null
if ("true".equalsIgnoreCase(string)) {
return Boolean.TRUE;
}
if ("false".equalsIgnoreCase(string)) {
return Boolean.FALSE;
}
if ("null".equalsIgnoreCase(string)) {
return JSONObject.NULL;
}
/*
* If it might be a number, try converting it. If a number cannot be
* produced, then the value will just be a string.
*/
char initial = string.charAt(0);
if ((initial >= '0' && initial <= '9') || initial == '-') {
try {
return stringToNumber(string);
} catch (Exception ignore) {
}
}
return string;
}
/**
* Convert a well-formed (but not necessarily valid) XML string into a
@@ -651,11 +710,11 @@ public class XML {
*/
public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration config) throws JSONException {
JSONObject jo = new JSONObject();
XMLTokener x = new XMLTokener(reader);
XMLTokener x = new XMLTokener(reader, config);
while (x.more()) {
x.skipPast("<");
if(x.more()) {
parse(x, jo, null, config);
parse(x, jo, null, config, 0);
}
}
return jo;
@@ -752,6 +811,28 @@ public class XML {
*/
public static String toString(final Object object, final String tagName, final XMLParserConfiguration config)
throws JSONException {
return toString(object, tagName, config, 0, 0);
}
/**
* Convert a JSONObject into a well-formed, element-normal XML string,
* either pretty print or single-lined depending on indent factor.
*
* @param object
* A JSONObject.
* @param tagName
* The optional name of the enclosing tag.
* @param config
* Configuration that can control output to XML.
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @param indent
* The current ident level in spaces.
* @return
* @throws JSONException
*/
private static String toString(final Object object, final String tagName, final XMLParserConfiguration config, int indentFactor, int indent)
throws JSONException {
StringBuilder sb = new StringBuilder();
JSONArray ja;
JSONObject jo;
@@ -761,9 +842,14 @@ public class XML {
// Emit <tagName>
if (tagName != null) {
sb.append(indent(indent));
sb.append('<');
sb.append(tagName);
sb.append('>');
if(indentFactor > 0){
sb.append("\n");
indent += indentFactor;
}
}
// Loop thru the keys.
@@ -806,31 +892,52 @@ public class XML {
sb.append('<');
sb.append(key);
sb.append('>');
sb.append(toString(val, null, config));
sb.append(toString(val, null, config, indentFactor, indent));
sb.append("</");
sb.append(key);
sb.append('>');
} else {
sb.append(toString(val, key, config));
sb.append(toString(val, key, config, indentFactor, indent));
}
}
} else if ("".equals(value)) {
if (config.isCloseEmptyTag()){
sb.append(indent(indent));
sb.append('<');
sb.append(key);
sb.append(">");
sb.append("</");
sb.append(key);
sb.append(">");
if (indentFactor > 0) {
sb.append("\n");
}
}else {
sb.append(indent(indent));
sb.append('<');
sb.append(key);
sb.append("/>");
if (indentFactor > 0) {
sb.append("\n");
}
}
// Emit a new tag <k>
} else {
sb.append(toString(value, key, config));
sb.append(toString(value, key, config, indentFactor, indent));
}
}
if (tagName != null) {
// Emit the </tagName> close tag
sb.append(indent(indent - indentFactor));
sb.append("</");
sb.append(tagName);
sb.append('>');
if(indentFactor > 0){
sb.append("\n");
}
}
return sb.toString();
@@ -849,15 +956,85 @@ public class XML {
// XML does not have good support for arrays. If an array
// appears in a place where XML is lacking, synthesize an
// <array> element.
sb.append(toString(val, tagName == null ? "array" : tagName, config));
sb.append(toString(val, tagName == null ? "array" : tagName, config, indentFactor, indent));
}
return sb.toString();
}
string = (object == null) ? "null" : escape(object.toString());
return (tagName == null) ? "\"" + string + "\""
: (string.length() == 0) ? "<" + tagName + "/>" : "<" + tagName
+ ">" + string + "</" + tagName + ">";
string = (object == null) ? "null" : escape(object.toString());
String indentationSuffix = (indentFactor > 0) ? "\n" : "";
if(tagName == null){
return indent(indent) + "\"" + string + "\"" + indentationSuffix;
} else if(string.length() == 0){
return indent(indent) + "<" + tagName + "/>" + indentationSuffix;
} else {
return indent(indent) + "<" + tagName
+ ">" + string + "</" + tagName + ">" + indentationSuffix;
}
}
/**
* Convert a JSONObject into a well-formed, pretty printed element-normal XML string.
*
* @param object
* A JSONObject.
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @return A string.
* @throws JSONException Thrown if there is an error parsing the string
*/
public static String toString(Object object, int indentFactor){
return toString(object, null, XMLParserConfiguration.ORIGINAL, indentFactor);
}
/**
* Convert a JSONObject into a well-formed, pretty printed element-normal XML string.
*
* @param object
* A JSONObject.
* @param tagName
* The optional name of the enclosing tag.
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @return A string.
* @throws JSONException Thrown if there is an error parsing the string
*/
public static String toString(final Object object, final String tagName, int indentFactor) {
return toString(object, tagName, XMLParserConfiguration.ORIGINAL, indentFactor);
}
/**
* Convert a JSONObject into a well-formed, pretty printed element-normal XML string.
*
* @param object
* A JSONObject.
* @param tagName
* The optional name of the enclosing tag.
* @param config
* Configuration that can control output to XML.
* @param indentFactor
* The number of spaces to add to each level of indentation.
* @return A string.
* @throws JSONException Thrown if there is an error parsing the string
*/
public static String toString(final Object object, final String tagName, final XMLParserConfiguration config, int indentFactor)
throws JSONException {
return toString(object, tagName, config, indentFactor, 0);
}
/**
* Return a String consisting of a number of space characters specified by indent
*
* @param indent
* The number of spaces to be appended to the String.
* @return
*/
private static final String indent(int indent) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indent; i++) {
sb.append(' ');
}
return sb.toString();
}
}

View File

@@ -15,7 +15,13 @@ import java.util.Set;
* @author AylwardJ
*/
@SuppressWarnings({""})
public class XMLParserConfiguration {
public class XMLParserConfiguration extends ParserConfiguration {
/**
* The default maximum nesting depth when parsing a XML document to JSON.
*/
// public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; // We could override
/** Original Configuration of the XML Parser. */
public static final XMLParserConfiguration ORIGINAL
= new XMLParserConfiguration();
@@ -23,12 +29,6 @@ public class XMLParserConfiguration {
public static final XMLParserConfiguration KEEP_STRINGS
= new XMLParserConfiguration().withKeepStrings(true);
/**
* When parsing the XML into JSON, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*/
private boolean keepStrings;
/**
* 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
@@ -43,6 +43,13 @@ public class XMLParserConfiguration {
*/
private boolean convertNilAttributeToNull;
/**
* When creating an XML from JSON Object, an empty tag by default will self-close.
* If it has to be closed explicitly, with empty content between start and end tag,
* this flag is to be turned on.
*/
private boolean closeEmptyTag;
/**
* This will allow type conversion for values in XML if xsi:type attribute is defined
*/
@@ -54,16 +61,26 @@ public class XMLParserConfiguration {
*/
private Set<String> forceList;
/**
* Flag to indicate whether white space should be trimmed when parsing XML.
* The default behaviour is to trim white space. When this is set to false, inputting XML
* with tags that are the same as the value of cDataTagName is unsupported. It is recommended to set cDataTagName
* to a distinct value in this case.
*/
private boolean shouldTrimWhiteSpace;
/**
* Default parser configuration. Does not keep strings (tries to implicitly convert
* values), and the CDATA Tag Name is "content".
* values), and the CDATA Tag Name is "content". Trims whitespace.
*/
public XMLParserConfiguration () {
this.keepStrings = false;
super();
this.cDataTagName = "content";
this.convertNilAttributeToNull = false;
this.xsiTypeMap = Collections.emptyMap();
this.forceList = Collections.emptySet();
this.shouldTrimWhiteSpace = true;
}
/**
@@ -106,7 +123,7 @@ public class XMLParserConfiguration {
*/
@Deprecated
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName) {
this.keepStrings = keepStrings;
super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH);
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = false;
}
@@ -125,7 +142,7 @@ public class XMLParserConfiguration {
*/
@Deprecated
public XMLParserConfiguration (final boolean keepStrings, final String cDataTagName, final boolean convertNilAttributeToNull) {
this.keepStrings = keepStrings;
super(keepStrings, DEFAULT_MAXIMUM_NESTING_DEPTH);
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull;
}
@@ -141,14 +158,18 @@ public class XMLParserConfiguration {
* @param xsiTypeMap <code>new HashMap<String, XMLXsiTypeConverter<?>>()</code> to parse values with attribute
* xsi:type="integer" as integer, xsi:type="string" as string
* @param forceList <code>new HashSet<String>()</code> to parse the provided tags' values as arrays
* @param maxNestingDepth <code>int</code> to limit the nesting depth
* @param closeEmptyTag <code>boolean</code> to turn on explicit end tag for tag with empty value
*/
private XMLParserConfiguration (final boolean keepStrings, final String cDataTagName,
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList ) {
this.keepStrings = keepStrings;
final boolean convertNilAttributeToNull, final Map<String, XMLXsiTypeConverter<?>> xsiTypeMap, final Set<String> forceList,
final int maxNestingDepth, final boolean closeEmptyTag) {
super(keepStrings, maxNestingDepth);
this.cDataTagName = cDataTagName;
this.convertNilAttributeToNull = convertNilAttributeToNull;
this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap);
this.forceList = Collections.unmodifiableSet(forceList);
this.closeEmptyTag = closeEmptyTag;
}
/**
@@ -161,23 +182,17 @@ public class XMLParserConfiguration {
// item, a new map instance should be created and if possible each value in the
// map should be cloned as well. If the values of the map are known to also
// be immutable, then a shallow clone of the map is acceptable.
return new XMLParserConfiguration(
final XMLParserConfiguration config = new XMLParserConfiguration(
this.keepStrings,
this.cDataTagName,
this.convertNilAttributeToNull,
this.xsiTypeMap,
this.forceList
this.forceList,
this.maxNestingDepth,
this.closeEmptyTag
);
}
/**
* When parsing the XML into JSON, specifies if values 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 isKeepStrings() {
return this.keepStrings;
config.shouldTrimWhiteSpace = this.shouldTrimWhiteSpace;
return config;
}
/**
@@ -189,10 +204,10 @@ public class XMLParserConfiguration {
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
@Override
public XMLParserConfiguration withKeepStrings(final boolean newVal) {
XMLParserConfiguration newConfig = this.clone();
newConfig.keepStrings = newVal;
return newConfig;
return super.withKeepStrings(newVal);
}
/**
@@ -297,4 +312,61 @@ public class XMLParserConfiguration {
newConfig.forceList = Collections.unmodifiableSet(cloneForceList);
return newConfig;
}
/**
* Defines the maximum nesting depth that the parser will descend before throwing an exception
* when parsing the XML into JSON. The default max nesting depth is 512, which means the parser
* will throw a JsonException if the maximum depth is reached.
* Using any negative value as a parameter is equivalent to setting no limit to the nesting depth,
* which means the parses will go as deep as the maximum call stack size allows.
* @param maxNestingDepth the maximum nesting depth allowed to the XML parser
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@SuppressWarnings("unchecked")
@Override
public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) {
return super.withMaxNestingDepth(maxNestingDepth);
}
/**
* To enable explicit end tag with empty value.
* @param closeEmptyTag new value for the closeEmptyTag property
* @return same instance of configuration with empty tag config updated
*/
public XMLParserConfiguration withCloseEmptyTag(boolean closeEmptyTag){
XMLParserConfiguration clonedConfiguration = this.clone();
clonedConfiguration.closeEmptyTag = closeEmptyTag;
return clonedConfiguration;
}
/**
* Sets whether whitespace should be trimmed inside of tags. *NOTE* Do not use this if
* you expect your XML tags to have names that are the same as cDataTagName as this is unsupported.
* cDataTagName should be set to a distinct value in these cases.
* @param shouldTrimWhiteSpace boolean to set trimming on or off. Off is default.
* @return same instance of configuration with empty tag config updated
*/
public XMLParserConfiguration withShouldTrimWhitespace(boolean shouldTrimWhiteSpace){
XMLParserConfiguration clonedConfiguration = this.clone();
clonedConfiguration.shouldTrimWhiteSpace = shouldTrimWhiteSpace;
return clonedConfiguration;
}
/**
* Checks if the parser should automatically close empty XML tags.
*
* @return {@code true} if empty XML tags should be automatically closed, {@code false} otherwise.
*/
public boolean isCloseEmptyTag() {
return this.closeEmptyTag;
}
/**
* Checks if the parser should trim white spaces from XML content.
*
* @return {@code true} if white spaces should be trimmed, {@code false} otherwise.
*/
public boolean shouldTrimWhiteSpace() {
return this.shouldTrimWhiteSpace;
}
}

View File

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

View File

@@ -42,5 +42,12 @@ Public Domain.
* @param <T> return type of convert method
*/
public interface XMLXsiTypeConverter<T> {
/**
* Converts an XML xsi:type attribute value to the specified type {@code T}.
*
* @param value The string representation of the XML xsi:type attribute value to be converted.
* @return An object of type {@code T} representing the converted value.
*/
T convert(String value);
}

View File

@@ -24,14 +24,13 @@ public class CDLTest {
* String of lines where the column names are in the first row,
* and all subsequent rows are values. All keys and values should be legal.
*/
String lines = new String(
"Col 1, Col 2, \tCol 3, Col 4, Col 5, Col 6, Col 7\n" +
private static final String LINES = "Col 1, Col 2, \tCol 3, Col 4, Col 5, Col 6, Col 7\n" +
"val1, val2, val3, val4, val5, val6, val7\n" +
"1, 2, 3, 4\t, 5, 6, 7\n" +
"true, false, true, true, false, false, false\n" +
"0.23, 57.42, 5e27, -234.879, 2.34e5, 0.0, 9e-3\n" +
"\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", va\'l6, val7\n"
);
"\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", va'l6, val7\n";
/**
* CDL.toJSONArray() adds all values as strings, with no filtering or
@@ -39,12 +38,11 @@ public class CDLTest {
* values all must be quoted in the cases where the JSONObject parsing
* might normally convert the value into a non-string.
*/
String expectedLines = new String(
"[{Col 1:val1, Col 2:val2, Col 3:val3, Col 4:val4, Col 5:val5, Col 6:val6, Col 7:val7}, "+
"{Col 1:\"1\", Col 2:\"2\", Col 3:\"3\", Col 4:\"4\", Col 5:\"5\", Col 6:\"6\", Col 7:\"7\"}, "+
"{Col 1:\"true\", Col 2:\"false\", Col 3:\"true\", Col 4:\"true\", Col 5:\"false\", Col 6:\"false\", Col 7:\"false\"}, "+
"{Col 1:\"0.23\", Col 2:\"57.42\", Col 3:\"5e27\", Col 4:\"-234.879\", Col 5:\"2.34e5\", Col 6:\"0.0\", Col 7:\"9e-3\"}, "+
"{Col 1:\"va\tl1\", Col 2:\"v\bal2\", Col 3:val3, Col 4:\"val\f4\", Col 5:val5, Col 6:va\'l6, Col 7:val7}]");
private static final String EXPECTED_LINES = "[{Col 1:val1, Col 2:val2, Col 3:val3, Col 4:val4, Col 5:val5, Col 6:val6, Col 7:val7}, " +
"{Col 1:\"1\", Col 2:\"2\", Col 3:\"3\", Col 4:\"4\", Col 5:\"5\", Col 6:\"6\", Col 7:\"7\"}, " +
"{Col 1:\"true\", Col 2:\"false\", Col 3:\"true\", Col 4:\"true\", Col 5:\"false\", Col 6:\"false\", Col 7:\"false\"}, " +
"{Col 1:\"0.23\", Col 2:\"57.42\", Col 3:\"5e27\", Col 4:\"-234.879\", Col 5:\"2.34e5\", Col 6:\"0.0\", Col 7:\"9e-3\"}, " +
"{Col 1:\"va\tl1\", Col 2:\"v\bal2\", Col 3:val3, Col 4:\"val\f4\", Col 5:val5, Col 6:va'l6, Col 7:val7}]";
/**
* Attempts to create a JSONArray from a null string.
@@ -194,8 +192,7 @@ public class CDLTest {
public void emptyString() {
String emptyStr = "";
JSONArray jsonArray = CDL.toJSONArray(emptyStr);
assertTrue("CDL should return null when the input string is empty",
jsonArray == null);
assertNull("CDL should return null when the input string is empty", jsonArray);
}
/**
@@ -254,7 +251,7 @@ public class CDLTest {
jsonObject.put("Col \r1", "V1");
// \r will be filtered from value
jsonObject.put("Col 2", "V2\r");
assertTrue("expected length should be 1",jsonArray.length() == 1);
assertEquals("expected length should be 1", 1, jsonArray.length());
String cdlStr = CDL.toString(jsonArray);
jsonObject = jsonArray.getJSONObject(0);
assertTrue(cdlStr.contains("\"Col 1\""));
@@ -268,8 +265,15 @@ public class CDLTest {
*/
@Test
public void textToJSONArray() {
JSONArray jsonArray = CDL.toJSONArray(this.lines);
JSONArray expectedJsonArray = new JSONArray(this.expectedLines);
JSONArray jsonArray = CDL.toJSONArray(LINES);
JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES);
Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray);
}
@Test
public void textToJSONArrayPipeDelimited() {
char delimiter = '|';
JSONArray jsonArray = CDL.toJSONArray(LINES.replaceAll(",", String.valueOf(delimiter)), delimiter);
JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES);
Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray);
}
@@ -293,10 +297,24 @@ public class CDLTest {
*/
@Test
public void textToJSONArrayAndBackToString() {
JSONArray jsonArray = CDL.toJSONArray(this.lines);
JSONArray jsonArray = CDL.toJSONArray(LINES);
String jsonStr = CDL.toString(jsonArray);
JSONArray finalJsonArray = CDL.toJSONArray(jsonStr);
JSONArray expectedJsonArray = new JSONArray(this.expectedLines);
JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES);
Util.compareActualVsExpectedJsonArrays(finalJsonArray, expectedJsonArray);
}
/**
* Create a JSONArray from a string of lines,
* then convert to string and then back to JSONArray
* with a custom delimiter
*/
@Test
public void textToJSONArrayAndBackToStringCustomDelimiter() {
JSONArray jsonArray = CDL.toJSONArray(LINES, ',');
String jsonStr = CDL.toString(jsonArray, ';');
JSONArray finalJsonArray = CDL.toJSONArray(jsonStr, ';');
JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES);
Util.compareActualVsExpectedJsonArrays(finalJsonArray, expectedJsonArray);
}

View File

@@ -6,7 +6,6 @@ Public Domain.
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -29,10 +28,13 @@ import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONParserConfiguration;
import org.json.JSONPointerException;
import org.json.JSONString;
import org.json.JSONTokener;
import org.json.ParserConfiguration;
import org.json.junit.data.MyJsonString;
import org.junit.Ignore;
import org.junit.Test;
import com.jayway.jsonpath.Configuration;
@@ -119,7 +121,7 @@ public class JSONArrayTest {
* Expects a JSONException.
*/
@Test
public void emptStr() {
public void emptyStr() {
String str = "";
try {
assertNull("Should throw an exception", new JSONArray(str));
@@ -369,16 +371,16 @@ public class JSONArrayTest {
"hello".equals(jsonArray.getString(4)));
// doubles
assertTrue("Array double",
new Double(23.45e-4).equals(jsonArray.getDouble(5)));
Double.valueOf(23.45e-4).equals(jsonArray.getDouble(5)));
assertTrue("Array string double",
new Double(23.45).equals(jsonArray.getDouble(6)));
Double.valueOf(23.45).equals(jsonArray.getDouble(6)));
assertTrue("Array double can be float",
new Float(23.45e-4f).equals(jsonArray.getFloat(5)));
Float.valueOf(23.45e-4f).equals(jsonArray.getFloat(5)));
// ints
assertTrue("Array value int",
new Integer(42).equals(jsonArray.getInt(7)));
Integer.valueOf(42).equals(jsonArray.getInt(7)));
assertTrue("Array value string int",
new Integer(43).equals(jsonArray.getInt(8)));
Integer.valueOf(43).equals(jsonArray.getInt(8)));
// nested objects
JSONArray nestedJsonArray = jsonArray.getJSONArray(9);
assertTrue("Array value JSONArray", nestedJsonArray != null);
@@ -386,9 +388,9 @@ public class JSONArrayTest {
assertTrue("Array value JSONObject", nestedJsonObject != null);
// longs
assertTrue("Array value long",
new Long(0).equals(jsonArray.getLong(11)));
Long.valueOf(0).equals(jsonArray.getLong(11)));
assertTrue("Array value string long",
new Long(-1).equals(jsonArray.getLong(12)));
Long.valueOf(-1).equals(jsonArray.getLong(12)));
assertTrue("Array value null", jsonArray.isNull(-1));
Util.checkJSONArrayMaps(jsonArray);
@@ -461,6 +463,20 @@ public class JSONArrayTest {
Util.checkJSONArrayMaps(jsonArray);
}
/**
* The JSON parser is permissive of unambiguous unquoted keys and values.
* Such JSON text should be allowed, even if it does not strictly conform
* to the spec. However, after being parsed, toString() should emit strictly
* conforming JSON text.
*/
@Test
public void unquotedText() {
String str = "[value1, something!, (parens), foo@bar.com, 23, 23+45]";
JSONArray jsonArray = new JSONArray(str);
List<Object> expected = Arrays.asList("value1", "something!", "(parens)", "foo@bar.com", 23, "23+45");
assertEquals(expected, jsonArray.toList());
}
/**
* Exercise JSONArray.join() by converting a JSONArray into a
* comma-separated string. Since this is very nearly a JSON document,
@@ -538,43 +554,75 @@ public class JSONArrayTest {
assertTrue("Array opt boolean implicit default",
Boolean.FALSE == jsonArray.optBoolean(-1));
assertTrue("Array opt boolean object",
Boolean.TRUE.equals(jsonArray.optBooleanObject(0)));
assertTrue("Array opt boolean object default",
Boolean.FALSE.equals(jsonArray.optBooleanObject(-1, Boolean.FALSE)));
assertTrue("Array opt boolean object implicit default",
Boolean.FALSE.equals(jsonArray.optBooleanObject(-1)));
assertTrue("Array opt double",
new Double(23.45e-4).equals(jsonArray.optDouble(5)));
Double.valueOf(23.45e-4).equals(jsonArray.optDouble(5)));
assertTrue("Array opt double default",
new Double(1).equals(jsonArray.optDouble(0, 1)));
Double.valueOf(1).equals(jsonArray.optDouble(0, 1)));
assertTrue("Array opt double default implicit",
new Double(jsonArray.optDouble(99)).isNaN());
Double.valueOf(jsonArray.optDouble(99)).isNaN());
assertTrue("Array opt double object",
Double.valueOf(23.45e-4).equals(jsonArray.optDoubleObject(5)));
assertTrue("Array opt double object default",
Double.valueOf(1).equals(jsonArray.optDoubleObject(0, 1D)));
assertTrue("Array opt double object default implicit",
jsonArray.optDoubleObject(99).isNaN());
assertTrue("Array opt float",
new Float(23.45e-4).equals(jsonArray.optFloat(5)));
Float.valueOf(Double.valueOf(23.45e-4).floatValue()).equals(jsonArray.optFloat(5)));
assertTrue("Array opt float default",
new Float(1).equals(jsonArray.optFloat(0, 1)));
Float.valueOf(1).equals(jsonArray.optFloat(0, 1)));
assertTrue("Array opt float default implicit",
new Float(jsonArray.optFloat(99)).isNaN());
Float.valueOf(jsonArray.optFloat(99)).isNaN());
assertTrue("Array opt float object",
Float.valueOf(23.45e-4F).equals(jsonArray.optFloatObject(5)));
assertTrue("Array opt float object default",
Float.valueOf(1).equals(jsonArray.optFloatObject(0, 1F)));
assertTrue("Array opt float object default implicit",
jsonArray.optFloatObject(99).isNaN());
assertTrue("Array opt Number",
BigDecimal.valueOf(23.45e-4).equals(jsonArray.optNumber(5)));
assertTrue("Array opt Number default",
new Double(1).equals(jsonArray.optNumber(0, 1d)));
Double.valueOf(1).equals(jsonArray.optNumber(0, 1d)));
assertTrue("Array opt Number default implicit",
new Double(jsonArray.optNumber(99,Double.NaN).doubleValue()).isNaN());
Double.valueOf(jsonArray.optNumber(99,Double.NaN).doubleValue()).isNaN());
assertTrue("Array opt int",
new Integer(42).equals(jsonArray.optInt(7)));
Integer.valueOf(42).equals(jsonArray.optInt(7)));
assertTrue("Array opt int default",
new Integer(-1).equals(jsonArray.optInt(0, -1)));
Integer.valueOf(-1).equals(jsonArray.optInt(0, -1)));
assertTrue("Array opt int default implicit",
0 == jsonArray.optInt(0));
assertTrue("Array opt int object",
Integer.valueOf(42).equals(jsonArray.optIntegerObject(7)));
assertTrue("Array opt int object default",
Integer.valueOf(-1).equals(jsonArray.optIntegerObject(0, -1)));
assertTrue("Array opt int object default implicit",
Integer.valueOf(0).equals(jsonArray.optIntegerObject(0)));
JSONArray nestedJsonArray = jsonArray.optJSONArray(9);
assertTrue("Array opt JSONArray", nestedJsonArray != null);
assertTrue("Array opt JSONArray default",
assertTrue("Array opt JSONArray null",
null == jsonArray.optJSONArray(99));
assertTrue("Array opt JSONArray default",
"value".equals(jsonArray.optJSONArray(99, new JSONArray("[\"value\"]")).getString(0)));
JSONObject nestedJsonObject = jsonArray.optJSONObject(10);
assertTrue("Array opt JSONObject", nestedJsonObject != null);
assertTrue("Array opt JSONObject default",
assertTrue("Array opt JSONObject null",
null == jsonArray.optJSONObject(99));
assertTrue("Array opt JSONObject default",
"value".equals(jsonArray.optJSONObject(99, new JSONObject("{\"key\":\"value\"}")).getString("key")));
assertTrue("Array opt long",
0 == jsonArray.optLong(11));
@@ -583,6 +631,13 @@ public class JSONArrayTest {
assertTrue("Array opt long default implicit",
0 == jsonArray.optLong(-1));
assertTrue("Array opt long object",
Long.valueOf(0).equals(jsonArray.optLongObject(11)));
assertTrue("Array opt long object default",
Long.valueOf(-2).equals(jsonArray.optLongObject(-1, -2L)));
assertTrue("Array opt long object default implicit",
Long.valueOf(0).equals(jsonArray.optLongObject(-1)));
assertTrue("Array opt string",
"hello".equals(jsonArray.optString(4)));
assertTrue("Array opt string default implicit",
@@ -600,10 +655,15 @@ public class JSONArrayTest {
public void optStringConversion(){
JSONArray ja = new JSONArray("[\"123\",\"true\",\"false\"]");
assertTrue("unexpected optBoolean value",ja.optBoolean(1,false)==true);
assertTrue("unexpected optBooleanObject value",Boolean.valueOf(true).equals(ja.optBooleanObject(1,false)));
assertTrue("unexpected optBoolean value",ja.optBoolean(2,true)==false);
assertTrue("unexpected optBooleanObject value",Boolean.valueOf(false).equals(ja.optBooleanObject(2,true)));
assertTrue("unexpected optInt value",ja.optInt(0,0)==123);
assertTrue("unexpected optIntegerObject value",Integer.valueOf(123).equals(ja.optIntegerObject(0,0)));
assertTrue("unexpected optLong value",ja.optLong(0,0)==123);
assertTrue("unexpected optLongObject value",Long.valueOf(123).equals(ja.optLongObject(0,0L)));
assertTrue("unexpected optDouble value",ja.optDouble(0,0.0)==123.0);
assertTrue("unexpected optDoubleObject value",Double.valueOf(123.0).equals(ja.optDoubleObject(0,0.0)));
assertTrue("unexpected optBigInteger value",ja.optBigInteger(0,BigInteger.ZERO).compareTo(new BigInteger("123"))==0);
assertTrue("unexpected optBigDecimal value",ja.optBigDecimal(0,BigDecimal.ZERO).compareTo(new BigDecimal("123"))==0);
Util.checkJSONArrayMaps(ja);
@@ -972,12 +1032,12 @@ public class JSONArrayTest {
assertTrue("Array double [23.45e-4]",
new BigDecimal("0.002345").equals(it.next()));
assertTrue("Array string double",
new Double(23.45).equals(Double.parseDouble((String)it.next())));
Double.valueOf(23.45).equals(Double.parseDouble((String)it.next())));
assertTrue("Array value int",
new Integer(42).equals(it.next()));
Integer.valueOf(42).equals(it.next()));
assertTrue("Array value string int",
new Integer(43).equals(Integer.parseInt((String)it.next())));
Integer.valueOf(43).equals(Integer.parseInt((String)it.next())));
JSONArray nestedJsonArray = (JSONArray)it.next();
assertTrue("Array value JSONArray", nestedJsonArray != null);
@@ -986,9 +1046,9 @@ public class JSONArrayTest {
assertTrue("Array value JSONObject", nestedJsonObject != null);
assertTrue("Array value long",
new Long(0).equals(((Number) it.next()).longValue()));
Long.valueOf(0).equals(((Number) it.next()).longValue()));
assertTrue("Array value string long",
new Long(-1).equals(Long.parseLong((String) it.next())));
Long.valueOf(-1).equals(Long.parseLong((String) it.next())));
assertTrue("should be at end of array", !it.hasNext());
Util.checkJSONArraysMaps(new ArrayList<JSONArray>(Arrays.asList(
jsonArray, nestedJsonArray
@@ -1327,14 +1387,15 @@ public class JSONArrayTest {
/**
* Tests for stack overflow. See https://github.com/stleary/JSON-java/issues/654
*/
@Ignore("This test relies on system constraints and may not always pass. See: https://github.com/stleary/JSON-java/issues/821")
@Test(expected = JSONException.class)
public void issue654StackOverflowInputWellFormed() {
//String input = new String(java.util.Base64.getDecoder().decode(base64Bytes));
final InputStream resourceAsStream = JSONObjectTest.class.getClassLoader().getResourceAsStream("Issue654WellFormedArray.json");
final InputStream resourceAsStream = JSONArrayTest.class.getClassLoader().getResourceAsStream("Issue654WellFormedArray.json");
JSONTokener tokener = new JSONTokener(resourceAsStream);
JSONArray json_input = new JSONArray(tokener);
assertNotNull(json_input);
fail("Excepected Exception.");
fail("Excepected Exception due to stack overflow.");
Util.checkJSONArrayMaps(json_input);
}
@@ -1358,4 +1419,92 @@ public class JSONArrayTest {
.put(2);
assertFalse(ja1.similar(ja3));
}
@Test(expected = JSONException.class)
public void testRecursiveDepth() {
HashMap<String, Object> map = new HashMap<>();
map.put("t", map);
new JSONArray().put(map);
}
@Test(expected = JSONException.class)
public void testRecursiveDepthAtPosition() {
HashMap<String, Object> map = new HashMap<>();
map.put("t", map);
new JSONArray().put(0, map);
}
@Test(expected = JSONException.class)
public void testRecursiveDepthArray() {
ArrayList<Object> array = new ArrayList<>();
array.add(array);
new JSONArray(array);
}
@Test
public void testRecursiveDepthAtPositionDefaultObject() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray().put(0, map);
}
@Test
public void testRecursiveDepthAtPosition1000Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1000);
new JSONArray().put(0, map, new JSONParserConfiguration().withMaxNestingDepth(1000));
}
@Test(expected = JSONException.class)
public void testRecursiveDepthAtPosition1001Object() {
HashMap<String, Object> map = JSONObjectTest.buildNestedMap(1001);
new JSONArray().put(0, map);
}
@Test(expected = JSONException.class)
public void testRecursiveDepthArrayLimitedMaps() {
ArrayList<Object> array = new ArrayList<>();
array.add(array);
new JSONArray(array);
}
@Test
public void testRecursiveDepthArrayForDefaultLevels() {
ArrayList<Object> array = buildNestedArray(ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
new JSONArray(array, new JSONParserConfiguration());
}
@Test
public void testRecursiveDepthArrayFor1000Levels() {
try {
ArrayList<Object> array = buildNestedArray(1000);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
new JSONArray(array, parserConfiguration);
} catch (StackOverflowError e) {
String javaVersion = System.getProperty("java.version");
if (javaVersion.startsWith("11.")) {
System.out.println(
"testRecursiveDepthArrayFor1000Levels() allowing intermittent stackoverflow, Java Version: "
+ javaVersion);
} else {
String errorStr = "testRecursiveDepthArrayFor1000Levels() unexpected stackoverflow, Java Version: "
+ javaVersion;
System.out.println(errorStr);
throw new RuntimeException(errorStr);
}
}
}
@Test(expected = JSONException.class)
public void testRecursiveDepthArrayFor1001Levels() {
ArrayList<Object> array = buildNestedArray(1001);
new JSONArray(array);
}
public static ArrayList<Object> buildNestedArray(int maxDepth) {
if (maxDepth <= 0) {
return new ArrayList<>();
}
ArrayList<Object> nestedArray = new ArrayList<>();
nestedArray.add(buildNestedArray(maxDepth - 1));
return nestedArray;
}
}

View File

@@ -762,8 +762,8 @@ public class JSONMLTest {
final String xml = JSONML.toString(originalObject);
final JSONObject revertedObject = JSONML.toJSONObject(xml, false);
final String newJson = revertedObject.toString();
assertTrue("JSON Objects are not similar",originalObject.similar(revertedObject));
assertEquals("original JSON does not equal the new JSON",originalJson, newJson);
assertTrue("JSON Objects are not similar", originalObject.similar(revertedObject));
assertTrue("JSON Strings are not similar", new JSONObject(originalJson).similar(new JSONObject(newJson)));
}
// these tests do not pass for the following reasons:
@@ -833,4 +833,157 @@ public class JSONMLTest {
ex.getMessage());
}
}
@Test
public void testToJSONArrayMaxNestingDepthOf42IsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
final int maxNestingDepth = 42;
try {
JSONML.toJSONArray(wayTooLongMalformedXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
}
}
@Test
public void testToJSONArrayMaxNestingDepthIsRespectedWithValidXML() {
final String perfectlyFineXML = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
final int maxNestingDepth = 1;
try {
JSONML.toJSONArray(perfectlyFineXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
}
}
@Test
public void testToJSONArrayMaxNestingDepthWithValidFittingXML() {
final String perfectlyFineXML = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
final int maxNestingDepth = 3;
try {
JSONML.toJSONArray(perfectlyFineXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed as its maximum depth fits the maxNestingDepth " +
"parameter of the JSONMLParserConfiguration used");
}
}
@Test
public void testToJSONObjectMaxDefaultNestingDepthIsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
try {
JSONML.toJSONObject(wayTooLongMalformedXML, JSONMLParserConfiguration.ORIGINAL);
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + JSONMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH));
}
}
@Test
public void testToJSONObjectUnlimitedNestingDepthIsPossible() {
int actualDepth = JSONMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH +10;
final String deeperThanDefaultMax = new String(new char[actualDepth]).replace("\0", "<a>") +
"value" +
new String(new char[actualDepth]).replace("\0", "</a>");
try {
JSONML.toJSONObject(deeperThanDefaultMax, JSONMLParserConfiguration.ORIGINAL
.withMaxNestingDepth(JSONMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed beyond the default maximum depth if maxNestingDepth " +
"parameter is set to -1 in JSONMLParserConfiguration");
}
}
@Test
public void testToJSONObjectMaxNestingDepthOf42IsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
final int maxNestingDepth = 42;
try {
JSONML.toJSONObject(wayTooLongMalformedXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
}
}
@Test
public void testToJSONObjectMaxNestingDepthIsRespectedWithValidXML() {
final String perfectlyFineXML = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
final int maxNestingDepth = 1;
try {
JSONML.toJSONObject(perfectlyFineXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
}
}
@Test
public void testToJSONObjectMaxNestingDepthWithValidFittingXML() {
final String perfectlyFineXML = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
final int maxNestingDepth = 3;
try {
JSONML.toJSONObject(perfectlyFineXML, JSONMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed as its maximum depth fits the maxNestingDepth " +
"parameter of the JSONMLParserConfiguration used");
}
}
}

View File

@@ -0,0 +1,146 @@
package org.json.junit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(value = Parameterized.class)
public class JSONObjectNumberTest {
private final String objectString;
private Integer value = 50;
@Parameters(name = "{index}: {0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{"{value:50}", 1},
{"{value:50.0}", 1},
{"{value:5e1}", 1},
{"{value:5E1}", 1},
{"{value:5e1}", 1},
{"{value:'50'}", 1},
{"{value:-50}", -1},
{"{value:-50.0}", -1},
{"{value:-5e1}", -1},
{"{value:-5E1}", -1},
{"{value:-5e1}", -1},
{"{value:'-50'}", -1}
// JSON does not support octal or hex numbers;
// see https://stackoverflow.com/a/52671839/6323312
// "{value:062}", // octal 50
// "{value:0x32}" // hex 50
});
}
public JSONObjectNumberTest(String objectString, int resultIsNegative) {
this.objectString = objectString;
this.value *= resultIsNegative;
}
private JSONObject object;
@Before
public void setJsonObject() {
object = new JSONObject(objectString);
}
@Test
public void testGetNumber() {
assertEquals(value.intValue(), object.getNumber("value").intValue());
}
@Test
public void testGetBigDecimal() {
assertTrue(BigDecimal.valueOf(value).compareTo(object.getBigDecimal("value")) == 0);
}
@Test
public void testGetBigInteger() {
assertEquals(BigInteger.valueOf(value), object.getBigInteger("value"));
}
@Test
public void testGetFloat() {
assertEquals(value.floatValue(), object.getFloat("value"), 0.0f);
}
@Test
public void testGetDouble() {
assertEquals(value.doubleValue(), object.getDouble("value"), 0.0d);
}
@Test
public void testGetInt() {
assertEquals(value.intValue(), object.getInt("value"));
}
@Test
public void testGetLong() {
assertEquals(value.longValue(), object.getLong("value"));
}
@Test
public void testOptNumber() {
assertEquals(value.intValue(), object.optNumber("value").intValue());
}
@Test
public void testOptBigDecimal() {
assertTrue(BigDecimal.valueOf(value).compareTo(object.optBigDecimal("value", null)) == 0);
}
@Test
public void testOptBigInteger() {
assertEquals(BigInteger.valueOf(value), object.optBigInteger("value", null));
}
@Test
public void testOptFloat() {
assertEquals(value.floatValue(), object.optFloat("value"), 0.0f);
}
@Test
public void testOptFloatObject() {
assertEquals((Float) value.floatValue(), object.optFloatObject("value"), 0.0f);
}
@Test
public void testOptDouble() {
assertEquals(value.doubleValue(), object.optDouble("value"), 0.0d);
}
@Test
public void testOptDoubleObject() {
assertEquals((Double) value.doubleValue(), object.optDoubleObject("value"), 0.0d);
}
@Test
public void testOptInt() {
assertEquals(value.intValue(), object.optInt("value"));
}
@Test
public void testOptIntegerObject() {
assertEquals((Integer) value.intValue(), object.optIntegerObject("value"));
}
@Test
public void testOptLong() {
assertEquals(value.longValue(), object.optLong("value"));
}
@Test
public void testOptLongObject() {
assertEquals((Long) value.longValue(), object.optLongObject("value"));
}
}

View File

@@ -9,6 +9,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
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;
import static org.mockito.Mockito.mock;
@@ -30,8 +31,10 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONPointerException;
import org.json.JSONParserConfiguration;
import org.json.JSONString;
import org.json.JSONTokener;
import org.json.ParserConfiguration;
import org.json.XML;
import org.json.junit.data.BrokenToString;
import org.json.junit.data.ExceptionalBean;
@@ -53,8 +56,8 @@ import org.json.junit.data.RecursiveBeanEquals;
import org.json.junit.data.Singleton;
import org.json.junit.data.SingletonEnum;
import org.json.junit.data.WeirdList;
import org.junit.Ignore;
import org.junit.Test;
import org.json.junit.Util;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
@@ -206,13 +209,17 @@ public class JSONObjectTest {
*/
@Test
public void unquotedText() {
String str = "{key1:value1, key2:42}";
String str = "{key1:value1, key2:42, 1.2 : 3.4, -7e5 : something!}";
JSONObject jsonObject = new JSONObject(str);
String textStr = jsonObject.toString();
assertTrue("expected key1", textStr.contains("\"key1\""));
assertTrue("expected value1", textStr.contains("\"value1\""));
assertTrue("expected key2", textStr.contains("\"key2\""));
assertTrue("expected 42", textStr.contains("42"));
assertTrue("expected 1.2", textStr.contains("\"1.2\""));
assertTrue("expected 3.4", textStr.contains("3.4"));
assertTrue("expected -7E+5", textStr.contains("\"-7E+5\""));
assertTrue("expected something!", textStr.contains("\"something!\""));
Util.checkJSONObjectMaps(jsonObject);
}
@@ -231,6 +238,11 @@ public class JSONObjectTest {
assert 26315000000253009L == actualLong : "Incorrect key value. Got "
+ actualLong + " expected " + str;
final Long actualLongObject = json.optLongObject("key");
assert actualLongObject != 0L : "Unable to extract Long value for string " + str;
assert Long.valueOf(26315000000253009L).equals(actualLongObject) : "Incorrect key value. Got "
+ actualLongObject + " expected " + str;
final String actualString = json.optString("key");
assert str.equals(actualString) : "Incorrect key value. Got "
+ actualString + " expected " + str;
@@ -299,12 +311,12 @@ public class JSONObjectTest {
@Test
public void jsonObjectByMap() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("trueKey", new Boolean(true));
map.put("falseKey", new Boolean(false));
map.put("trueKey", Boolean.valueOf(true));
map.put("falseKey", Boolean.valueOf(false));
map.put("stringKey", "hello world!");
map.put("escapeStringKey", "h\be\tllo w\u1234orld!");
map.put("intKey", new Long(42));
map.put("doubleKey", new Double(-23.45e67));
map.put("intKey", Long.valueOf(42));
map.put("doubleKey", Double.valueOf(-23.45e67));
JSONObject jsonObject = new JSONObject(map);
// validate JSON
@@ -565,13 +577,13 @@ public class JSONObjectTest {
@Test
public void jsonObjectByMapWithNullValue() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("trueKey", new Boolean(true));
map.put("falseKey", new Boolean(false));
map.put("trueKey", Boolean.valueOf(true));
map.put("falseKey", Boolean.valueOf(false));
map.put("stringKey", "hello world!");
map.put("nullKey", null);
map.put("escapeStringKey", "h\be\tllo w\u1234orld!");
map.put("intKey", new Long(42));
map.put("doubleKey", new Double(-23.45e67));
map.put("intKey", Long.valueOf(42));
map.put("doubleKey", Double.valueOf(-23.45e67));
JSONObject jsonObject = new JSONObject(map);
// validate JSON
@@ -621,9 +633,9 @@ public class JSONObjectTest {
assertTrue("expected 42", Integer.valueOf("42").equals(jsonObject.query("/intKey")));
assertTrue("expected -23.45e7", Double.valueOf("-23.45e7").equals(jsonObject.query("/doubleKey")));
// sorry, mockito artifact
assertTrue("expected 2 callbacks items", ((List<?>)(JsonPath.read(doc, "$.callbacks"))).size() == 2);
assertTrue("expected 0 handler items", ((Map<?,?>)(JsonPath.read(doc, "$.callbacks[0].handler"))).size() == 0);
assertTrue("expected 0 callbacks[1] items", ((Map<?,?>)(JsonPath.read(doc, "$.callbacks[1]"))).size() == 0);
assertTrue("expected 2 mockitoInterceptor items", ((Map<?,?>)(JsonPath.read(doc, "$.mockitoInterceptor"))).size() == 2);
assertTrue("expected 0 mockitoInterceptor.serializationSupport items",
((Map<?,?>)(JsonPath.read(doc, "$.mockitoInterceptor.serializationSupport"))).size() == 0);
Util.checkJSONObjectMaps(jsonObject);
}
@@ -866,9 +878,11 @@ public class JSONObjectTest {
JSONObject jsonObject = new JSONObject(str);
assertTrue("trueKey should be true", jsonObject.getBoolean("trueKey"));
assertTrue("opt trueKey should be true", jsonObject.optBoolean("trueKey"));
assertTrue("opt trueKey should be true", jsonObject.optBooleanObject("trueKey"));
assertTrue("falseKey should be false", !jsonObject.getBoolean("falseKey"));
assertTrue("trueStrKey should be true", jsonObject.getBoolean("trueStrKey"));
assertTrue("trueStrKey should be true", jsonObject.optBoolean("trueStrKey"));
assertTrue("trueStrKey should be true", jsonObject.optBooleanObject("trueStrKey"));
assertTrue("falseStrKey should be false", !jsonObject.getBoolean("falseStrKey"));
assertTrue("stringKey should be string",
jsonObject.getString("stringKey").equals("hello world!"));
@@ -884,6 +898,10 @@ public class JSONObjectTest {
jsonObject.optDouble("doubleKey") == -23.45e7);
assertTrue("opt doubleKey with Default should be double",
jsonObject.optDouble("doubleStrKey", Double.NaN) == 1);
assertTrue("opt doubleKey should be Double",
Double.valueOf(-23.45e7).equals(jsonObject.optDoubleObject("doubleKey")));
assertTrue("opt doubleKey with Default should be Double",
Double.valueOf(1).equals(jsonObject.optDoubleObject("doubleStrKey", Double.NaN)));
assertTrue("opt negZeroKey should be a Double",
jsonObject.opt("negZeroKey") instanceof Double);
assertTrue("get negZeroKey should be a Double",
@@ -896,6 +914,10 @@ public class JSONObjectTest {
Double.compare(jsonObject.optDouble("negZeroKey"), -0.0d) == 0);
assertTrue("opt negZeroStrKey with Default should be double",
Double.compare(jsonObject.optDouble("negZeroStrKey"), -0.0d) == 0);
assertTrue("opt negZeroKey should be Double",
Double.valueOf(-0.0d).equals(jsonObject.optDoubleObject("negZeroKey")));
assertTrue("opt negZeroStrKey with Default should be Double",
Double.valueOf(-0.0d).equals(jsonObject.optDoubleObject("negZeroStrKey")));
assertTrue("optNumber negZeroKey should be -0.0",
Double.compare(jsonObject.optNumber("negZeroKey").doubleValue(), -0.0d) == 0);
assertTrue("optNumber negZeroStrKey should be -0.0",
@@ -904,10 +926,18 @@ public class JSONObjectTest {
jsonObject.optFloat("doubleKey") == -23.45e7f);
assertTrue("optFloat doubleKey with Default should be float",
jsonObject.optFloat("doubleStrKey", Float.NaN) == 1f);
assertTrue("optFloat doubleKey should be Float",
Float.valueOf(-23.45e7f).equals(jsonObject.optFloatObject("doubleKey")));
assertTrue("optFloat doubleKey with Default should be Float",
Float.valueOf(1f).equals(jsonObject.optFloatObject("doubleStrKey", Float.NaN)));
assertTrue("intKey should be int",
jsonObject.optInt("intKey") == 42);
assertTrue("opt intKey should be int",
jsonObject.optInt("intKey", 0) == 42);
assertTrue("intKey should be Integer",
Integer.valueOf(42).equals(jsonObject.optIntegerObject("intKey")));
assertTrue("opt intKey should be Integer",
Integer.valueOf(42).equals(jsonObject.optIntegerObject("intKey", 0)));
assertTrue("opt intKey with default should be int",
jsonObject.getInt("intKey") == 42);
assertTrue("intStrKey should be int",
@@ -918,6 +948,10 @@ public class JSONObjectTest {
jsonObject.optLong("longKey") == 1234567890123456789L);
assertTrue("opt longKey with default should be long",
jsonObject.optLong("longKey", 0) == 1234567890123456789L);
assertTrue("opt longKey should be Long",
Long.valueOf(1234567890123456789L).equals(jsonObject.optLongObject("longKey")));
assertTrue("opt longKey with default should be Long",
Long.valueOf(1234567890123456789L).equals(jsonObject.optLongObject("longKey", 0L)));
assertTrue("longStrKey should be long",
jsonObject.getLong("longStrKey") == 987654321098765432L);
assertTrue("optNumber int should return Integer",
@@ -969,7 +1003,7 @@ public class JSONObjectTest {
assertTrue( "0.2 should be a BigDecimal!",
JSONObject.stringToValue( "0.2" ) instanceof BigDecimal );
assertTrue( "Doubles should be BigDecimal, even when incorrectly converting floats!",
JSONObject.stringToValue( new Double( "0.2f" ).toString() ) instanceof BigDecimal );
JSONObject.stringToValue( Double.valueOf( "0.2f" ).toString() ) instanceof BigDecimal );
/**
* This test documents a need for BigDecimal conversion.
*/
@@ -979,13 +1013,13 @@ public class JSONObjectTest {
assertTrue( "1 should be an Integer!",
JSONObject.stringToValue( "1" ) instanceof Integer );
assertTrue( "Integer.MAX_VALUE should still be an Integer!",
JSONObject.stringToValue( new Integer( Integer.MAX_VALUE ).toString() ) instanceof Integer );
JSONObject.stringToValue( Integer.valueOf( Integer.MAX_VALUE ).toString() ) instanceof Integer );
assertTrue( "Large integers should be a Long!",
JSONObject.stringToValue( Long.valueOf(((long)Integer.MAX_VALUE) + 1 ) .toString() ) instanceof Long );
assertTrue( "Long.MAX_VALUE should still be an Integer!",
JSONObject.stringToValue( new Long( Long.MAX_VALUE ).toString() ) instanceof Long );
JSONObject.stringToValue( Long.valueOf( Long.MAX_VALUE ).toString() ) instanceof Long );
String str = new BigInteger( new Long( Long.MAX_VALUE ).toString() ).add( BigInteger.ONE ).toString();
String str = new BigInteger( Long.valueOf( Long.MAX_VALUE ).toString() ).add( BigInteger.ONE ).toString();
assertTrue( "Really large integers currently evaluate to BigInteger",
JSONObject.stringToValue(str).equals(new BigInteger("9223372036854775808")));
}
@@ -1058,7 +1092,7 @@ public class JSONObjectTest {
assertTrue( "negativeFraction currently evaluates to double -0.01",
jsonObject.get( "negativeFraction" ).equals(BigDecimal.valueOf(-0.01)));
assertTrue( "tooManyZerosFraction currently evaluates to double 0.001",
jsonObject.get( "tooManyZerosFraction" ).equals(BigDecimal.valueOf(0.001)));
jsonObject.optLong( "tooManyZerosFraction" )==0);
assertTrue( "negativeHexFloat currently evaluates to double -3.99951171875",
jsonObject.get( "negativeHexFloat" ).equals(Double.valueOf(-3.99951171875)));
assertTrue("hexFloat currently evaluates to double 4.9E-324",
@@ -1232,8 +1266,8 @@ public class JSONObjectTest {
String key30 = "key30";
String key31 = "key31";
JSONObject jsonObject = new JSONObject();
jsonObject.put(key30, new Double(3.0));
jsonObject.put(key31, new Double(3.1));
jsonObject.put(key30, Double.valueOf(3.0));
jsonObject.put(key31, Double.valueOf(3.1));
assertTrue("3.0 should remain a double",
jsonObject.getDouble(key30) == 3);
@@ -1686,19 +1720,19 @@ public class JSONObjectTest {
*/
assertFalse("Document unexpected behaviour with explicit type-casting float as double!", (double)0.2f == 0.2d );
assertFalse("Document unexpected behaviour with implicit type-cast!", 0.2f == 0.2d );
Double d1 = new Double( 1.1f );
Double d2 = new Double( "1.1f" );
Double d1 = Double.valueOf( 1.1f );
Double d2 = Double.valueOf( "1.1f" );
assertFalse( "Document implicit type cast from float to double before calling Double(double d) constructor", d1.equals( d2 ) );
assertTrue( "Correctly converting float to double via base10 (string) representation!", new Double( 3.1d ).equals( new Double( new Float( 3.1f ).toString() ) ) );
assertTrue( "Correctly converting float to double via base10 (string) representation!", Double.valueOf( 3.1d ).equals( Double.valueOf( Float.valueOf( 3.1f ).toString() ) ) );
// Pinpointing the not so obvious "buggy" conversion from float to double in JSONObject
JSONObject jo = new JSONObject();
jo.put( "bug", 3.1f ); // will call put( String key, double value ) with implicit and "buggy" type-cast from float to double
assertFalse( "The java-compiler did add some zero bits for you to the mantissa (unexpected, but well documented)", jo.get( "bug" ).equals( new Double( 3.1d ) ) );
assertFalse( "The java-compiler did add some zero bits for you to the mantissa (unexpected, but well documented)", jo.get( "bug" ).equals( Double.valueOf( 3.1d ) ) );
JSONObject inc = new JSONObject();
inc.put( "bug", new Float( 3.1f ) ); // This will put in instance of Float into JSONObject, i.e. call put( String key, Object value )
inc.put( "bug", Float.valueOf( 3.1f ) ); // This will put in instance of Float into JSONObject, i.e. call put( String key, Object value )
assertTrue( "Everything is ok here!", inc.get( "bug" ) instanceof Float );
inc.increment( "bug" ); // after adding 1, increment will call put( String key, double value ) with implicit and "buggy" type-cast from float to double!
// this.put(key, (Float) value + 1);
@@ -1942,7 +1976,7 @@ public class JSONObjectTest {
@Test
public void jsonObjectToStringSuppressWarningOnCastToMap() {
JSONObject jsonObject = new JSONObject();
Map<String, String> map = new HashMap();
Map<String, String> map = new HashMap<>();
map.put("abc", "def");
jsonObject.put("key", map);
@@ -2000,7 +2034,9 @@ public class JSONObjectTest {
"}";
JSONObject jsonObject = new JSONObject(jsonObjectStr);
assertTrue("jsonObject valueToString() incorrect",
JSONObject.valueToString(jsonObject).equals(jsonObject.toString()));
new JSONObject(JSONObject.valueToString(jsonObject))
.similar(new JSONObject(jsonObject.toString()))
);
String jsonArrayStr =
"[1,2,3]";
JSONArray jsonArray = new JSONArray(jsonArrayStr);
@@ -2011,16 +2047,17 @@ public class JSONObjectTest {
map.put("key2", "val2");
map.put("key3", "val3");
assertTrue("map valueToString() incorrect",
jsonObject.toString().equals(JSONObject.valueToString(map)));
new JSONObject(jsonObject.toString())
.similar(new JSONObject(JSONObject.valueToString(map))));
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(new Integer(1));
collection.add(new Integer(2));
collection.add(new Integer(3));
collection.add(Integer.valueOf(1));
collection.add(Integer.valueOf(2));
collection.add(Integer.valueOf(3));
assertTrue("collection valueToString() expected: "+
jsonArray.toString()+ " actual: "+
JSONObject.valueToString(collection),
jsonArray.toString().equals(JSONObject.valueToString(collection)));
Integer[] array = { new Integer(1), new Integer(2), new Integer(3) };
Integer[] array = { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3) };
assertTrue("array valueToString() incorrect",
jsonArray.toString().equals(JSONObject.valueToString(array)));
Util.checkJSONObjectMaps(jsonObject);
@@ -2058,7 +2095,7 @@ public class JSONObjectTest {
JSONObject.NULL == JSONObject.wrap(null));
// wrap(Integer) returns Integer
Integer in = new Integer(1);
Integer in = Integer.valueOf(1);
assertTrue("Integer wrap() incorrect",
in == JSONObject.wrap(in));
@@ -2085,9 +2122,9 @@ public class JSONObjectTest {
// wrap collection returns JSONArray
Collection<Integer> collection = new ArrayList<Integer>();
collection.add(new Integer(1));
collection.add(new Integer(2));
collection.add(new Integer(3));
collection.add(Integer.valueOf(1));
collection.add(Integer.valueOf(2));
collection.add(Integer.valueOf(3));
JSONArray jsonArray = (JSONArray) (JSONObject.wrap(collection));
// validate JSON
@@ -2098,7 +2135,7 @@ public class JSONObjectTest {
assertTrue("expected 3", Integer.valueOf(3).equals(jsonArray.query("/2")));
// wrap Array returns JSONArray
Integer[] array = { new Integer(1), new Integer(2), new Integer(3) };
Integer[] array = { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3) };
JSONArray integerArrayJsonArray = (JSONArray)(JSONObject.wrap(array));
// validate JSON
@@ -2198,6 +2235,51 @@ public class JSONObjectTest {
"Expected a ',' or '}' at 15 [character 16 line 1]",
e.getMessage());
}
try {
// key is a nested map
String str = "{{\"foo\": \"bar\"}: \"baz\"}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Missing value at 1 [character 2 line 1]",
e.getMessage());
}
try {
// key is a nested array containing a map
String str = "{\"a\": 1, [{\"foo\": \"bar\"}]: \"baz\"}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Missing value at 9 [character 10 line 1]",
e.getMessage());
}
try {
// key contains }
String str = "{foo}: 2}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Expected a ':' after a key at 5 [character 6 line 1]",
e.getMessage());
}
try {
// key contains ]
String str = "{foo]: 2}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"Expected a ':' after a key at 5 [character 6 line 1]",
e.getMessage());
}
try {
// \0 after ,
String str = "{\"myKey\":true, \0\"myOtherKey\":false}";
assertNull("Expected an exception",new JSONObject(str));
} catch (JSONException e) {
assertEquals("Expecting an exception message",
"A JSONObject text must end with '}' at 15 [character 16 line 1]",
e.getMessage());
}
try {
// append to wrong key
String str = "{\"myKey\":true, \"myOtherKey\":false}";
@@ -2465,20 +2547,32 @@ public class JSONObjectTest {
BigInteger.TEN.compareTo(jsonObject.optBigInteger("myKey",BigInteger.TEN ))==0);
assertTrue("optBoolean() should return default boolean",
jsonObject.optBoolean("myKey", true));
assertTrue("optBooleanObject() should return default Boolean",
jsonObject.optBooleanObject("myKey", true));
assertTrue("optInt() should return default int",
42 == jsonObject.optInt("myKey", 42));
assertTrue("optIntegerObject() should return default Integer",
Integer.valueOf(42).equals(jsonObject.optIntegerObject("myKey", 42)));
assertTrue("optEnum() should return default Enum",
MyEnum.VAL1.equals(jsonObject.optEnum(MyEnum.class, "myKey", MyEnum.VAL1)));
assertTrue("optJSONArray() should return null ",
null==jsonObject.optJSONArray("myKey"));
assertTrue("optJSONArray() should return default JSONArray",
"value".equals(jsonObject.optJSONArray("myKey", new JSONArray("[\"value\"]")).getString(0)));
assertTrue("optJSONObject() should return default JSONObject ",
jsonObject.optJSONObject("myKey", new JSONObject("{\"testKey\":\"testValue\"}")).getString("testKey").equals("testValue"));
assertTrue("optLong() should return default long",
42l == jsonObject.optLong("myKey", 42l));
assertTrue("optLongObject() should return default Long",
Long.valueOf(42l).equals(jsonObject.optLongObject("myKey", 42l)));
assertTrue("optDouble() should return default double",
42.3d == jsonObject.optDouble("myKey", 42.3d));
assertTrue("optDoubleObject() should return default Double",
Double.valueOf(42.3d).equals(jsonObject.optDoubleObject("myKey", 42.3d)));
assertTrue("optFloat() should return default float",
42.3f == jsonObject.optFloat("myKey", 42.3f));
assertTrue("optFloatObject() should return default Float",
Float.valueOf(42.3f).equals(jsonObject.optFloatObject("myKey", 42.3f)));
assertTrue("optNumber() should return default Number",
42l == jsonObject.optNumber("myKey", Long.valueOf(42)).longValue());
assertTrue("optString() should return default string",
@@ -2502,20 +2596,32 @@ public class JSONObjectTest {
BigInteger.TEN.compareTo(jsonObject.optBigInteger("myKey",BigInteger.TEN ))==0);
assertTrue("optBoolean() should return default boolean",
jsonObject.optBoolean("myKey", true));
assertTrue("optBooleanObject() should return default Boolean",
jsonObject.optBooleanObject("myKey", true));
assertTrue("optInt() should return default int",
42 == jsonObject.optInt("myKey", 42));
assertTrue("optIntegerObject() should return default Integer",
Integer.valueOf(42).equals(jsonObject.optIntegerObject("myKey", 42)));
assertTrue("optEnum() should return default Enum",
MyEnum.VAL1.equals(jsonObject.optEnum(MyEnum.class, "myKey", MyEnum.VAL1)));
assertTrue("optJSONArray() should return default JSONArray",
"value".equals(jsonObject.optJSONArray("myKey", new JSONArray("[\"value\"]")).getString(0)));
assertTrue("optJSONArray() should return null ",
null==jsonObject.optJSONArray("myKey"));
assertTrue("optJSONObject() should return default JSONObject ",
jsonObject.optJSONObject("myKey", new JSONObject("{\"testKey\":\"testValue\"}")).getString("testKey").equals("testValue"));
assertTrue("optLong() should return default long",
42l == jsonObject.optLong("myKey", 42l));
assertTrue("optLongObject() should return default Long",
Long.valueOf(42l).equals(jsonObject.optLongObject("myKey", 42l)));
assertTrue("optDouble() should return default double",
42.3d == jsonObject.optDouble("myKey", 42.3d));
assertTrue("optDoubleObject() should return default Double",
Double.valueOf(42.3d).equals(jsonObject.optDoubleObject("myKey", 42.3d)));
assertTrue("optFloat() should return default float",
42.3f == jsonObject.optFloat("myKey", 42.3f));
assertTrue("optFloatObject() should return default Float",
Float.valueOf(42.3f).equals(jsonObject.optFloatObject("myKey", 42.3f)));
assertTrue("optNumber() should return default Number",
42l == jsonObject.optNumber("myKey", Long.valueOf(42)).longValue());
assertTrue("optString() should return default string",
@@ -2530,11 +2636,17 @@ public class JSONObjectTest {
public void jsonObjectOptStringConversion() {
JSONObject jo = new JSONObject("{\"int\":\"123\",\"true\":\"true\",\"false\":\"false\"}");
assertTrue("unexpected optBoolean value",jo.optBoolean("true",false)==true);
assertTrue("unexpected optBooleanObject value",Boolean.valueOf(true).equals(jo.optBooleanObject("true",false)));
assertTrue("unexpected optBoolean value",jo.optBoolean("false",true)==false);
assertTrue("unexpected optBooleanObject value",Boolean.valueOf(false).equals(jo.optBooleanObject("false",true)));
assertTrue("unexpected optInt value",jo.optInt("int",0)==123);
assertTrue("unexpected optIntegerObject value",Integer.valueOf(123).equals(jo.optIntegerObject("int",0)));
assertTrue("unexpected optLong value",jo.optLong("int",0)==123l);
assertTrue("unexpected optLongObject value",Long.valueOf(123l).equals(jo.optLongObject("int",0L)));
assertTrue("unexpected optDouble value",jo.optDouble("int",0.0d)==123.0d);
assertTrue("unexpected optDoubleObject value",Double.valueOf(123.0d).equals(jo.optDoubleObject("int",0.0d)));
assertTrue("unexpected optFloat value",jo.optFloat("int",0.0f)==123.0f);
assertTrue("unexpected optFloatObject value",Float.valueOf(123.0f).equals(jo.optFloatObject("int",0.0f)));
assertTrue("unexpected optBigInteger value",jo.optBigInteger("int",BigInteger.ZERO).compareTo(new BigInteger("123"))==0);
assertTrue("unexpected optBigDecimal value",jo.optBigDecimal("int",BigDecimal.ZERO).compareTo(new BigDecimal("123"))==0);
assertTrue("unexpected optBigDecimal value",jo.optBigDecimal("int",BigDecimal.ZERO).compareTo(new BigDecimal("123"))==0);
@@ -2555,23 +2667,35 @@ public class JSONObjectTest {
assertEquals(new BigDecimal("19007199254740993.35481234487103587486413587843213584"), jo.optBigDecimal("largeNumber",null));
assertEquals(new BigInteger("19007199254740993"), jo.optBigInteger("largeNumber",null));
assertEquals(1.9007199254740992E16, jo.optDouble("largeNumber"),0.0);
assertEquals(1.9007199254740992E16, jo.optDoubleObject("largeNumber"),0.0);
assertEquals(1.90071995E16f, jo.optFloat("largeNumber"),0.0f);
assertEquals(1.90071995E16f, jo.optFloatObject("largeNumber"),0.0f);
assertEquals(19007199254740993l, jo.optLong("largeNumber"));
assertEquals(Long.valueOf(19007199254740993l), jo.optLongObject("largeNumber"));
assertEquals(1874919425, jo.optInt("largeNumber"));
assertEquals(Integer.valueOf(1874919425), jo.optIntegerObject("largeNumber"));
// conversion from a string
assertEquals(new BigDecimal("19007199254740993.35481234487103587486413587843213584"), jo.optBigDecimal("largeNumberStr",null));
assertEquals(new BigInteger("19007199254740993"), jo.optBigInteger("largeNumberStr",null));
assertEquals(1.9007199254740992E16, jo.optDouble("largeNumberStr"),0.0);
assertEquals(1.9007199254740992E16, jo.optDoubleObject("largeNumberStr"),0.0);
assertEquals(1.90071995E16f, jo.optFloat("largeNumberStr"),0.0f);
assertEquals(1.90071995E16f, jo.optFloatObject("largeNumberStr"),0.0f);
assertEquals(19007199254740993l, jo.optLong("largeNumberStr"));
assertEquals(Long.valueOf(19007199254740993l), jo.optLongObject("largeNumberStr"));
assertEquals(1874919425, jo.optInt("largeNumberStr"));
assertEquals(Integer.valueOf(1874919425), jo.optIntegerObject("largeNumberStr"));
// the integer portion of the actual value is larger than a double can hold.
assertNotEquals((long)Double.parseDouble("19007199254740993.35481234487103587486413587843213584"), jo.optLong("largeNumber"));
assertNotEquals(Long.valueOf((long)Double.parseDouble("19007199254740993.35481234487103587486413587843213584")), jo.optLongObject("largeNumber"));
assertNotEquals((int)Double.parseDouble("19007199254740993.35481234487103587486413587843213584"), jo.optInt("largeNumber"));
assertNotEquals(Integer.valueOf((int)Double.parseDouble("19007199254740993.35481234487103587486413587843213584")), jo.optIntegerObject("largeNumber"));
assertNotEquals((long)Double.parseDouble("19007199254740993.35481234487103587486413587843213584"), jo.optLong("largeNumberStr"));
assertNotEquals(Long.valueOf((long)Double.parseDouble("19007199254740993.35481234487103587486413587843213584")), jo.optLongObject("largeNumberStr"));
assertNotEquals((int)Double.parseDouble("19007199254740993.35481234487103587486413587843213584"), jo.optInt("largeNumberStr"));
assertNotEquals(Integer.valueOf((int)Double.parseDouble("19007199254740993.35481234487103587486413587843213584")), jo.optIntegerObject("largeNumberStr"));
assertEquals(19007199254740992l, (long)Double.parseDouble("19007199254740993.35481234487103587486413587843213584"));
assertEquals(2147483647, (int)Double.parseDouble("19007199254740993.35481234487103587486413587843213584"));
Util.checkJSONObjectMaps(jo);
@@ -3166,7 +3290,7 @@ public class JSONObjectTest {
@SuppressWarnings("boxing")
@Test
public void testGenericBean() {
GenericBean<Integer> bean = new GenericBean(42);
GenericBean<Integer> bean = new GenericBean<>(42);
final JSONObject jo = new JSONObject(bean);
assertEquals(jo.keySet().toString(), 8, jo.length());
assertEquals(42, jo.get("genericValue"));
@@ -3215,6 +3339,7 @@ public class JSONObjectTest {
* Sample test case from https://github.com/stleary/JSON-java/issues/531
* which verifies that no regression in double/BigDecimal support is present.
*/
@Test
public void testObjectToBigDecimal() {
double value = 1412078745.01074;
Reader reader = new StringReader("[{\"value\": " + value + "}]");
@@ -3479,6 +3604,7 @@ public class JSONObjectTest {
/**
* Tests for stack overflow. See https://github.com/stleary/JSON-java/issues/654
*/
@Ignore("This test relies on system constraints and may not always pass. See: https://github.com/stleary/JSON-java/issues/821")
@Test(expected = JSONException.class)
public void issue654StackOverflowInputWellFormed() {
//String input = new String(java.util.Base64.getDecoder().decode(base64Bytes));
@@ -3486,7 +3612,7 @@ public class JSONObjectTest {
JSONTokener tokener = new JSONTokener(resourceAsStream);
JSONObject json_input = new JSONObject(tokener);
assertNotNull(json_input);
fail("Excepected Exception.");
fail("Excepected Exception due to stack overflow.");
}
@Test
@@ -3509,4 +3635,159 @@ public class JSONObjectTest {
.put("b", 2);
assertFalse(jo1.similar(jo3));
}
private static final Number[] NON_FINITE_NUMBERS = { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN,
Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NaN };
@Test
public void issue713MapConstructorWithNonFiniteNumbers() {
for (Number nonFinite : NON_FINITE_NUMBERS) {
Map<String, Number> map = new HashMap<>();
map.put("a", nonFinite);
assertThrows(JSONException.class, () -> new JSONObject(map));
}
}
@Test
public void issue713BeanConstructorWithNonFiniteNumbers() {
for (Number nonFinite : NON_FINITE_NUMBERS) {
GenericBean<Number> bean = new GenericBean<>(nonFinite);
assertThrows(JSONException.class, () -> new JSONObject(bean));
}
}
@Test(expected = JSONException.class)
public void issue743SerializationMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("t", map);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}
@Test(expected = JSONException.class)
public void testCircularReferenceMultipleLevel() {
HashMap<String, Object> inside = new HashMap<>();
HashMap<String, Object> jsonObject = new HashMap<>();
inside.put("inside", jsonObject);
jsonObject.put("test", inside);
new JSONObject(jsonObject);
}
@Test
public void issue743SerializationMapWith512Objects() {
HashMap<String, Object> map = buildNestedMap(ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}
@Test
public void issue743SerializationMapWith1000Objects() {
HashMap<String, Object> map = buildNestedMap(1000);
JSONParserConfiguration parserConfiguration = new JSONParserConfiguration().withMaxNestingDepth(1000);
JSONObject object = new JSONObject(map, parserConfiguration);
String jsonString = object.toString();
}
@Test(expected = JSONException.class)
public void issue743SerializationMapWith1001Objects() {
HashMap<String, Object> map = buildNestedMap(1001);
JSONObject object = new JSONObject(map);
String jsonString = object.toString();
}
@Test(expected = JSONException.class)
public void testCircleReferenceFirstLevel() {
Map<Object, Object> jsonObject = new HashMap<>();
jsonObject.put("test", jsonObject);
new JSONObject(jsonObject, new JSONParserConfiguration());
}
@Test(expected = StackOverflowError.class)
public void testCircleReferenceMultiplyLevel_notConfigured_expectedStackOverflow() {
Map<Object, Object> inside = new HashMap<>();
Map<Object, Object> jsonObject = new HashMap<>();
inside.put("test", jsonObject);
jsonObject.put("test", inside);
new JSONObject(jsonObject, new JSONParserConfiguration().withMaxNestingDepth(99999));
}
@Test(expected = JSONException.class)
public void testCircleReferenceMultiplyLevel_configured_expectedJSONException() {
Map<Object, Object> inside = new HashMap<>();
Map<Object, Object> jsonObject = new HashMap<>();
inside.put("test", jsonObject);
jsonObject.put("test", inside);
new JSONObject(jsonObject, new JSONParserConfiguration());
}
@Test
public void testDifferentKeySameInstanceNotACircleReference() {
Map<Object, Object> map1 = new HashMap<>();
Map<Object, Object> map2 = new HashMap<>();
map1.put("test1", map2);
map1.put("test2", map2);
new JSONObject(map1);
}
@Test
public void clarifyCurrentBehavior() {
// Behavior documented in #653 optLong vs getLong inconsistencies
// This problem still exists.
// Internally, both number_1 and number_2 are stored as strings. This is reasonable since they are parsed as strings.
// However, getLong and optLong should return similar results
JSONObject json = new JSONObject("{\"number_1\":\"01234\", \"number_2\": \"332211\"}");
assertEquals(json.getLong("number_1"), 1234L);
assertEquals(json.optLong("number_1"), 0); //THIS VALUE IS NOT RETURNED AS A NUMBER
assertEquals(json.getLong("number_2"), 332211L);
assertEquals(json.optLong("number_2"), 332211L);
// Behavior documented in #826 JSONObject parsing 0-led numeric strings as ints
// After reverting the code, personId is stored as a string, and the behavior is as expected
String personId = "0123";
JSONObject j1 = new JSONObject("{personId: " + personId + "}");
assertEquals(j1.getString("personId"), "0123");
// Also #826. Here is input with missing quotes. Because of the leading zero, it should not be parsed as a number.
// This example was mentioned in the same ticket
// After reverting the code, personId is stored as a string, and the behavior is as expected
JSONObject j2 = new JSONObject("{\"personId\":0123}");
assertEquals(j2.getString("personId"), "0123");
// Behavior uncovered while working on the code
// All of the values are stored as strings except for hex4, which is stored as a number. This is probably incorrect
JSONObject j3 = new JSONObject("{ " +
"\"hex1\": \"010e4\", \"hex2\": \"00f0\", \"hex3\": \"0011\", " +
"\"hex4\": 00e0, \"hex5\": 00f0, \"hex6\": 0011 }");
assertEquals(j3.getString("hex1"), "010e4");
assertEquals(j3.getString("hex2"), "00f0");
assertEquals(j3.getString("hex3"), "0011");
assertEquals(j3.getLong("hex4"), 0, .1);
assertEquals(j3.getString("hex5"), "00f0");
assertEquals(j3.getString("hex6"), "0011");
}
/**
* Method to build nested map of max maxDepth
*
* @param maxDepth
* @return
*/
public static HashMap<String, Object> buildNestedMap(int maxDepth) {
if (maxDepth <= 0) {
return new HashMap<>();
}
HashMap<String, Object> nestedMap = new HashMap<>();
nestedMap.put("t", buildNestedMap(maxDepth - 1));
return nestedMap;
}
}

View File

@@ -72,8 +72,10 @@ public class JSONPointerTest {
@Test
public void queryByEmptyKeySubObject() {
assertEquals( "{\"\":\"empty key of an object with an empty key\",\"subKey\":\"Some" +
" other value\"}", query("/obj/").toString());
JSONObject json = new JSONObject("{\"\":\"empty key of an object with an empty key\",\"subKey\":\"Some" +
" other value\"}");
JSONObject obj = (JSONObject) query("/obj/");
assertTrue(json.similar(obj));
}
@Test

View File

@@ -313,4 +313,16 @@ public class JSONTokenerTest {
assertEquals(0, t2.next());
assertFalse(t2.more());
}
@Test
public void testAutoClose(){
Reader reader = new StringReader("some test string");
try {
JSONTokener tokener = new JSONTokener(reader);
tokener.close();
tokener.next();
} catch (Exception exception){
assertEquals("Stream closed", exception.getMessage());
}
}
}

View File

@@ -4,11 +4,6 @@ package org.json.junit;
Public Domain.
*/
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
@@ -27,6 +22,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import static org.junit.Assert.*;
/**
* Tests for JSON-Java XML.java with XMLParserConfiguration.java
@@ -557,6 +554,37 @@ public class XMLConfigurationTest {
assertEquals(actualXML, resultXML);
}
@Test
public void shouldHandleEmptyNodeValue()
{
JSONObject inputJSON = new JSONObject();
inputJSON.put("Emptyness", "");
String expectedXmlWithoutExplicitEndTag = "<Emptyness/>";
String expectedXmlWithExplicitEndTag = "<Emptyness></Emptyness>";
assertEquals(expectedXmlWithoutExplicitEndTag, XML.toString(inputJSON, null,
new XMLParserConfiguration().withCloseEmptyTag(false)));
assertEquals(expectedXmlWithExplicitEndTag, XML.toString(inputJSON, null,
new XMLParserConfiguration().withCloseEmptyTag(true)));
}
@Test
public void shouldKeepConfigurationIntactAndUpdateCloseEmptyTagChoice()
{
XMLParserConfiguration keepStrings = XMLParserConfiguration.KEEP_STRINGS;
XMLParserConfiguration keepStringsAndCloseEmptyTag = keepStrings.withCloseEmptyTag(true);
XMLParserConfiguration keepDigits = keepStringsAndCloseEmptyTag.withKeepStrings(false);
XMLParserConfiguration keepDigitsAndNoCloseEmptyTag = keepDigits.withCloseEmptyTag(false);
assertTrue(keepStrings.isKeepStrings());
assertFalse(keepStrings.isCloseEmptyTag());
assertTrue(keepStringsAndCloseEmptyTag.isKeepStrings());
assertTrue(keepStringsAndCloseEmptyTag.isCloseEmptyTag());
assertFalse(keepDigits.isKeepStrings());
assertTrue(keepDigits.isCloseEmptyTag());
assertFalse(keepDigitsAndNoCloseEmptyTag.isKeepStrings());
assertFalse(keepDigitsAndNoCloseEmptyTag.isCloseEmptyTag());
}
/**
* Investigate exactly how the "content" keyword works
*/
@@ -1052,6 +1080,29 @@ public class XMLConfigurationTest {
Util.compareActualVsExpectedJsonObjects(jsonObject, expetedJsonObject);
}
@Test
public void testMaxNestingDepthIsSet() {
XMLParserConfiguration xmlParserConfiguration = XMLParserConfiguration.ORIGINAL;
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(42);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), 42);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(0);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), 0);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(-31415926);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
xmlParserConfiguration = xmlParserConfiguration.withMaxNestingDepth(Integer.MIN_VALUE);
assertEquals(xmlParserConfiguration.getMaxNestingDepth(), XMLParserConfiguration.UNDEFINED_MAXIMUM_NESTING_DEPTH);
}
/**
* Convenience method, given an input string and expected result,
* convert to JSONObject and compare actual to expected result.

View File

@@ -18,16 +18,11 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.json.XML;
import org.json.XMLParserConfiguration;
import org.json.XMLXsiTypeConverter;
import org.json.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -921,7 +916,7 @@ public class XMLTest {
InputStream xmlStream = null;
try {
xmlStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue537.xml");
Reader xmlReader = new InputStreamReader(xmlStream);
Reader xmlReader = new InputStreamReader(xmlStream, Charset.forName("UTF-8"));
JSONObject actual = XML.toJSONObject(xmlReader, true);
InputStream jsonStream = null;
try {
@@ -1049,4 +1044,389 @@ public class XMLTest {
fail("Expected to be unable to modify the config");
} catch (Exception ignored) { }
}
@Test
public void testIndentComplicatedJsonObject(){
String str = "{\n" +
" \"success\": true,\n" +
" \"error\": null,\n" +
" \"response\": [\n" +
" {\n" +
" \"timestamp\": 1664917200,\n" +
" \"dateTimeISO\": \"2022-10-05T00:00:00+03:00\",\n" +
" \"loc\": {\n" +
" \"lat\": 39.91987,\n" +
" \"long\": 32.85427\n" +
" },\n" +
" \"place\": {\n" +
" \"name\": \"ankara\",\n" +
" \"state\": \"an\",\n" +
" \"country\": \"tr\"\n" +
" },\n" +
" \"profile\": {\n" +
" \"tz\": \"Europe/Istanbul\"\n" +
" },\n" +
" \"sun\": {\n" +
" \"rise\": 1664941721,\n" +
" \"riseISO\": \"2022-10-05T06:48:41+03:00\",\n" +
" \"set\": 1664983521,\n" +
" \"setISO\": \"2022-10-05T18:25:21+03:00\",\n" +
" \"transit\": 1664962621,\n" +
" \"transitISO\": \"2022-10-05T12:37:01+03:00\",\n" +
" \"midnightSun\": false,\n" +
" \"polarNight\": false,\n" +
" \"twilight\": {\n" +
" \"civilBegin\": 1664940106,\n" +
" \"civilBeginISO\": \"2022-10-05T06:21:46+03:00\",\n" +
" \"civilEnd\": 1664985136,\n" +
" \"civilEndISO\": \"2022-10-05T18:52:16+03:00\",\n" +
" \"nauticalBegin\": 1664938227,\n" +
" \"nauticalBeginISO\": \"2022-10-05T05:50:27+03:00\",\n" +
" \"nauticalEnd\": 1664987015,\n" +
" \"nauticalEndISO\": \"2022-10-05T19:23:35+03:00\",\n" +
" \"astronomicalBegin\": 1664936337,\n" +
" \"astronomicalBeginISO\": \"2022-10-05T05:18:57+03:00\",\n" +
" \"astronomicalEnd\": 1664988905,\n" +
" \"astronomicalEndISO\": \"2022-10-05T19:55:05+03:00\"\n" +
" }\n" +
" },\n" +
" \"moon\": {\n" +
" \"rise\": 1664976480,\n" +
" \"riseISO\": \"2022-10-05T16:28:00+03:00\",\n" +
" \"set\": 1664921520,\n" +
" \"setISO\": \"2022-10-05T01:12:00+03:00\",\n" +
" \"transit\": 1664994240,\n" +
" \"transitISO\": \"2022-10-05T21:24:00+03:00\",\n" +
" \"underfoot\": 1664949360,\n" +
" \"underfootISO\": \"2022-10-05T08:56:00+03:00\",\n" +
" \"phase\": {\n" +
" \"phase\": 0.3186,\n" +
" \"name\": \"waxing gibbous\",\n" +
" \"illum\": 71,\n" +
" \"age\": 9.41,\n" +
" \"angle\": 0.55\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
"}" ;
JSONObject jsonObject = new JSONObject(str);
String actualIndentedXmlString = XML.toString(jsonObject, 1);
JSONObject actualJsonObject = XML.toJSONObject(actualIndentedXmlString);
String expected = "<success>true</success>\n" +
"<response>\n" +
" <dateTimeISO>2022-10-05T00:00:00+03:00</dateTimeISO>\n" +
" <loc>\n" +
" <lat>39.91987</lat>\n" +
" <long>32.85427</long>\n" +
" </loc>\n" +
" <moon>\n" +
" <phase>\n" +
" <phase>0.3186</phase>\n" +
" <name>waxing gibbous</name>\n" +
" <angle>0.55</angle>\n" +
" <illum>71</illum>\n" +
" <age>9.41</age>\n" +
" </phase>\n" +
" <setISO>2022-10-05T01:12:00+03:00</setISO>\n" +
" <underfoot>1664949360</underfoot>\n" +
" <set>1664921520</set>\n" +
" <transit>1664994240</transit>\n" +
" <transitISO>2022-10-05T21:24:00+03:00</transitISO>\n" +
" <riseISO>2022-10-05T16:28:00+03:00</riseISO>\n" +
" <rise>1664976480</rise>\n" +
" <underfootISO>2022-10-05T08:56:00+03:00</underfootISO>\n" +
" </moon>\n" +
" <profile>\n" +
" <tz>Europe/Istanbul</tz>\n" +
" </profile>\n" +
" <place>\n" +
" <country>tr</country>\n" +
" <name>ankara</name>\n" +
" <state>an</state>\n" +
" </place>\n" +
" <sun>\n" +
" <setISO>2022-10-05T18:25:21+03:00</setISO>\n" +
" <midnightSun>false</midnightSun>\n" +
" <set>1664983521</set>\n" +
" <transit>1664962621</transit>\n" +
" <polarNight>false</polarNight>\n" +
" <transitISO>2022-10-05T12:37:01+03:00</transitISO>\n" +
" <riseISO>2022-10-05T06:48:41+03:00</riseISO>\n" +
" <rise>1664941721</rise>\n" +
" <twilight>\n" +
" <civilEnd>1664985136</civilEnd>\n" +
" <astronomicalBegin>1664936337</astronomicalBegin>\n" +
" <astronomicalEnd>1664988905</astronomicalEnd>\n" +
" <astronomicalBeginISO>2022-10-05T05:18:57+03:00</astronomicalBeginISO>\n" +
" <civilBegin>1664940106</civilBegin>\n" +
" <nauticalEndISO>2022-10-05T19:23:35+03:00</nauticalEndISO>\n" +
" <astronomicalEndISO>2022-10-05T19:55:05+03:00</astronomicalEndISO>\n" +
" <nauticalBegin>1664938227</nauticalBegin>\n" +
" <nauticalEnd>1664987015</nauticalEnd>\n" +
" <nauticalBeginISO>2022-10-05T05:50:27+03:00</nauticalBeginISO>\n" +
" <civilBeginISO>2022-10-05T06:21:46+03:00</civilBeginISO>\n" +
" <civilEndISO>2022-10-05T18:52:16+03:00</civilEndISO>\n" +
" </twilight>\n" +
" </sun>\n" +
" <timestamp>1664917200</timestamp>\n" +
"</response>\n" +
"<error>null</error>\n";
JSONObject expectedJsonObject = XML.toJSONObject(expected);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void shouldCreateExplicitEndTagWithEmptyValueWhenConfigured(){
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
JSONObject jsonObject = new JSONObject(jsonString);
String expectedXmlString = "<encloser><outer><innerOne></innerOne><innerTwo>two</innerTwo></outer></encloser>";
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(true));
JSONObject actualJsonObject = XML.toJSONObject(xmlForm);
JSONObject expectedJsonObject = XML.toJSONObject(expectedXmlString);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void shouldNotCreateExplicitEndTagWithEmptyValueWhenNotConfigured(){
String jsonString = "{outer:{innerOne:\"\", innerTwo:\"two\"}}";
JSONObject jsonObject = new JSONObject(jsonString);
String expectedXmlString = "<encloser><outer><innerOne/><innerTwo>two</innerTwo></outer></encloser>";
String xmlForm = XML.toString(jsonObject,"encloser", new XMLParserConfiguration().withCloseEmptyTag(false));
JSONObject actualJsonObject = XML.toJSONObject(xmlForm);
JSONObject expectedJsonObject = XML.toJSONObject(expectedXmlString);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void testIndentSimpleJsonObject(){
String str = "{ \"employee\": { \n" +
" \"name\": \"sonoo\", \n" +
" \"salary\": 56000, \n" +
" \"married\": true \n" +
" }}";
JSONObject jsonObject = new JSONObject(str);
String actual = XML.toString(jsonObject, "Test", 2);
JSONObject actualJsonObject = XML.toJSONObject(actual);
String expected = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
JSONObject expectedJsonObject = XML.toJSONObject(expected);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void testIndentSimpleJsonArray(){
String str = "[ \n" +
" {\"name\":\"Ram\", \"email\":\"Ram@gmail.com\"}, \n" +
" {\"name\":\"Bob\", \"email\":\"bob32@gmail.com\"} \n" +
"] ";
JSONArray jsonObject = new JSONArray(str);
String actual = XML.toString(jsonObject, 2);
JSONObject actualJsonObject = XML.toJSONObject(actual);
String expected = "<array>\n" +
" <name>Ram</name>\n" +
" <email>Ram@gmail.com</email>\n" +
"</array>\n" +
"<array>\n" +
" <name>Bob</name>\n" +
" <email>bob32@gmail.com</email>\n" +
"</array>\n";
JSONObject expectedJsonObject = XML.toJSONObject(expected);
assertTrue(expectedJsonObject.similar(actualJsonObject));
}
@Test
public void testIndentComplicatedJsonObjectWithArrayAndWithConfig(){
try (InputStream jsonStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.json")) {
final JSONObject object = new JSONObject(new JSONTokener(jsonStream));
String actualString = XML.toString(object, null, XMLParserConfiguration.KEEP_STRINGS, 2);
try (InputStream xmlStream = XMLTest.class.getClassLoader().getResourceAsStream("Issue593.xml")) {
int bufferSize = 1024;
char[] buffer = new char[bufferSize];
StringBuilder expected = new StringBuilder();
Reader in = new InputStreamReader(xmlStream, "UTF-8");
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
expected.append(buffer, 0, numRead);
}
assertTrue(XML.toJSONObject(expected.toString()).similar(XML.toJSONObject(actualString)));
}
} catch (IOException e) {
fail("file writer error: " +e.getMessage());
}
}
@Test
public void testMaxNestingDepthOf42IsRespected() {
final String wayTooLongMalformedXML = new String(new char[6000]).replace("\0", "<a>");
final int maxNestingDepth = 42;
try {
XML.toJSONObject(wayTooLongMalformedXML, XMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
}
}
@Test
public void testMaxNestingDepthIsRespectedWithValidXML() {
final String perfectlyFineXML = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
final int maxNestingDepth = 1;
try {
XML.toJSONObject(perfectlyFineXML, XMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
fail("Expecting a JSONException");
} catch (JSONException e) {
assertTrue("Wrong throwable thrown: not expecting message <" + e.getMessage() + ">",
e.getMessage().startsWith("Maximum nesting depth of " + maxNestingDepth));
}
}
@Test
public void testMaxNestingDepthWithValidFittingXML() {
final String perfectlyFineXML = "<Test>\n" +
" <employee>\n" +
" <name>sonoo</name>\n" +
" <salary>56000</salary>\n" +
" <married>true</married>\n" +
" </employee>\n" +
"</Test>\n";
final int maxNestingDepth = 3;
try {
XML.toJSONObject(perfectlyFineXML, XMLParserConfiguration.ORIGINAL.withMaxNestingDepth(maxNestingDepth));
} catch (JSONException e) {
e.printStackTrace();
fail("XML document should be parsed as its maximum depth fits the maxNestingDepth " +
"parameter of the XMLParserConfiguration used");
}
}
@Test
public void testWithWhitespaceTrimmingDisabled() {
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false));
String expectedJsonString = "{\"testXml\":\" Test Whitespace String \t \"}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void testNestedWithWhitespaceTrimmingDisabled() {
String originalXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses>\n"+
" <address>\n"+
" <name> Sherlock Holmes </name>\n"+
" </address>\n"+
"</addresses>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false));
String expectedJsonString = "{\"addresses\":{\"address\":{\"name\":\" Sherlock Holmes \"}}}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void shouldTrimWhitespaceDoesNotSupportTagsEqualingCDataTagName() {
// When using withShouldTrimWhitespace = true, input containing tags with same name as cDataTagName is unsupported and should not be used in conjunction
String originalXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses>\n"+
" <address>\n"+
" <content> Sherlock Holmes </content>\n"+
" </address>\n"+
"</addresses>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(false).withcDataTagName("content"));
String expectedJsonString = "{\"addresses\":{\"address\":[[\"\\n \",\" Sherlock Holmes \",\"\\n \"]]}}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void shouldTrimWhitespaceEnabledDropsTagsEqualingCDataTagNameButValueRemains() {
String originalXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
"<addresses>\n"+
" <address>\n"+
" <content> Sherlock Holmes </content>\n"+
" </address>\n"+
"</addresses>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(true).withcDataTagName("content"));
String expectedJsonString = "{\"addresses\":{\"address\":\"Sherlock Holmes\"}}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void testWithWhitespaceTrimmingEnabled() {
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration().withShouldTrimWhitespace(true));
String expectedJsonString = "{\"testXml\":\"Test Whitespace String\"}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void testWithWhitespaceTrimmingEnabledByDefault() {
String originalXml = "<testXml> Test Whitespace String \t </testXml>";
JSONObject actualJson = XML.toJSONObject(originalXml, new XMLParserConfiguration());
String expectedJsonString = "{\"testXml\":\"Test Whitespace String\"}";
JSONObject expectedJson = new JSONObject(expectedJsonString);
Util.compareActualVsExpectedJsonObjects(actualJson,expectedJson);
}
@Test
public void clarifyCurrentBehavior() {
// Behavior documented in #826
// After reverting the code, amount is stored as numeric, and phone is stored as string
String str1 =
" <datatypes>\n" +
" <telephone>0123456789</telephone>\n" +
" <amount>0.1230</amount>\n" +
" <boolean>true</boolean>\n" +
" </datatypes>";
JSONObject jsonObject1 = XML.toJSONObject(str1,
new XMLParserConfiguration().withKeepStrings(false));
assertEquals(jsonObject1.getJSONObject("datatypes").getFloat("amount"), 0.123, .1);
assertEquals(jsonObject1.getJSONObject("datatypes").getString("telephone"), "0123456789");
// Behavior documented in #852
// After reverting the code, value is still stored as a number. This is due to how XML.isDecimalNotation() works
// and is probably a bug. JSONObject has a similar problem.
String str2 = "<color> <color_type>primary</color_type> <value>008E97</value> </color>";
JSONObject jsonObject2 = XML.toJSONObject(str2);
assertEquals(jsonObject2.getJSONObject("color").getLong("value"), 0e897, .1);
// Workaround for now is to use keepStrings
JSONObject jsonObject3 = XML.toJSONObject(str2, new XMLParserConfiguration().withKeepStrings(true));
assertEquals(jsonObject3.getJSONObject("color").getString("value"), "008E97");
}
}

View File

@@ -9,7 +9,7 @@ import java.io.StringReader;
* @param <T>
* generic number value
*/
public class GenericBean<T extends Number & Comparable<T>> implements MyBean {
public class GenericBean<T extends Number> implements MyBean {
/**
* @param genericValue
* value to initiate with

View File

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

View File

@@ -0,0 +1,704 @@
{
"success": true,
"error": null,
"response": [
{
"loc": {
"long": 31.25,
"lat": 30.063
},
"interval": "day",
"place": {
"name": "cairo",
"state": "qh",
"country": "eg"
},
"periods": [
{
"timestamp": 1665032400,
"validTime": "2022-10-06T07:00:00+02:00",
"dateTimeISO": "2022-10-06T07:00:00+02:00",
"maxTempC": 32,
"maxTempF": 90,
"minTempC": 19,
"minTempF": 66,
"avgTempC": 25,
"avgTempF": 78,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 32,
"maxFeelslikeF": 89,
"minFeelslikeC": 21,
"minFeelslikeF": 70,
"avgFeelslikeC": 26,
"avgFeelslikeF": 80,
"feelslikeC": 21,
"feelslikeF": 70,
"maxDewpointC": 17,
"maxDewpointF": 63,
"minDewpointC": 11,
"minDewpointF": 52,
"avgDewpointC": 14,
"avgDewpointF": 58,
"dewpointC": 17,
"dewpointF": 63,
"maxHumidity": 77,
"minHumidity": 29,
"humidity": 77,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1015,
"pressureIN": 29.97,
"windDir": "N",
"windDirDEG": 353,
"windSpeedKTS": 5,
"windSpeedKPH": 9,
"windSpeedMPH": 6,
"windGustKTS": 21,
"windGustKPH": 40,
"windGustMPH": 25,
"windDirMax": "NNW",
"windDirMaxDEG": 342,
"windSpeedMaxKTS": 9,
"windSpeedMaxKPH": 16,
"windSpeedMaxMPH": 10,
"windDirMin": "N",
"windDirMinDEG": 353,
"windSpeedMinKTS": 1,
"windSpeedMinKPH": 2,
"windSpeedMinMPH": 1,
"windDir80m": "N",
"windDir80mDEG": 11,
"windSpeed80mKTS": 12,
"windSpeed80mKPH": 22,
"windSpeed80mMPH": 13,
"windGust80mKTS": 22,
"windGust80mKPH": 41,
"windGust80mMPH": 25,
"windDirMax80m": "NNW",
"windDirMax80mDEG": 343,
"windSpeedMax80mKTS": 22,
"windSpeedMax80mKPH": 41,
"windSpeedMax80mMPH": 25,
"windDirMin80m": "E",
"windDirMin80mDEG": 95,
"windSpeedMin80mKTS": 8,
"windSpeedMin80mKPH": 15,
"windSpeedMin80mMPH": 10,
"sky": 22,
"cloudsCoded": "FW",
"weather": "Mostly Sunny",
"weatherCoded": [],
"weatherPrimary": "Mostly Sunny",
"weatherPrimaryCoded": "::FW",
"icon": "fair.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": 6,
"solradWM2": 5608,
"solradMinWM2": 0,
"solradMaxWM2": 778,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665028274,
"sunset": 1665070502,
"sunriseISO": "2022-10-06T05:51:14+02:00",
"sunsetISO": "2022-10-06T17:35:02+02:00"
},
{
"timestamp": 1665118800,
"validTime": "2022-10-07T07:00:00+02:00",
"dateTimeISO": "2022-10-07T07:00:00+02:00",
"maxTempC": 30,
"maxTempF": 86,
"minTempC": 19,
"minTempF": 66,
"avgTempC": 24,
"avgTempF": 76,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 29,
"maxFeelslikeF": 85,
"minFeelslikeC": 19,
"minFeelslikeF": 67,
"avgFeelslikeC": 24,
"avgFeelslikeF": 76,
"feelslikeC": 19,
"feelslikeF": 67,
"maxDewpointC": 15,
"maxDewpointF": 60,
"minDewpointC": 10,
"minDewpointF": 50,
"avgDewpointC": 12,
"avgDewpointF": 54,
"dewpointC": 15,
"dewpointF": 60,
"maxHumidity": 77,
"minHumidity": 30,
"humidity": 77,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1014,
"pressureIN": 29.95,
"windDir": "NW",
"windDirDEG": 325,
"windSpeedKTS": 1,
"windSpeedKPH": 2,
"windSpeedMPH": 1,
"windGustKTS": 16,
"windGustKPH": 29,
"windGustMPH": 18,
"windDirMax": "WNW",
"windDirMaxDEG": 298,
"windSpeedMaxKTS": 7,
"windSpeedMaxKPH": 13,
"windSpeedMaxMPH": 8,
"windDirMin": "NW",
"windDirMinDEG": 325,
"windSpeedMinKTS": 1,
"windSpeedMinKPH": 2,
"windSpeedMinMPH": 1,
"windDir80m": "NNW",
"windDir80mDEG": 347,
"windSpeed80mKTS": 6,
"windSpeed80mKPH": 10,
"windSpeed80mMPH": 6,
"windGust80mKTS": 20,
"windGust80mKPH": 37,
"windGust80mMPH": 23,
"windDirMax80m": "NW",
"windDirMax80mDEG": 316,
"windSpeedMax80mKTS": 20,
"windSpeedMax80mKPH": 37,
"windSpeedMax80mMPH": 23,
"windDirMin80m": "NNW",
"windDirMin80mDEG": 347,
"windSpeedMin80mKTS": 6,
"windSpeedMin80mKPH": 10,
"windSpeedMin80mMPH": 6,
"sky": 30,
"cloudsCoded": "FW",
"weather": "Mostly Sunny",
"weatherCoded": [],
"weatherPrimary": "Mostly Sunny",
"weatherPrimaryCoded": "::FW",
"icon": "fair.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": 6,
"solradWM2": 5486,
"solradMinWM2": 0,
"solradMaxWM2": 742,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665114710,
"sunset": 1665156831,
"sunriseISO": "2022-10-07T05:51:50+02:00",
"sunsetISO": "2022-10-07T17:33:51+02:00"
},
{
"timestamp": 1665205200,
"validTime": "2022-10-08T07:00:00+02:00",
"dateTimeISO": "2022-10-08T07:00:00+02:00",
"maxTempC": 30,
"maxTempF": 87,
"minTempC": 19,
"minTempF": 66,
"avgTempC": 25,
"avgTempF": 76,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 30,
"maxFeelslikeF": 86,
"minFeelslikeC": 19,
"minFeelslikeF": 67,
"avgFeelslikeC": 25,
"avgFeelslikeF": 76,
"feelslikeC": 19,
"feelslikeF": 67,
"maxDewpointC": 15,
"maxDewpointF": 59,
"minDewpointC": 11,
"minDewpointF": 52,
"avgDewpointC": 13,
"avgDewpointF": 56,
"dewpointC": 15,
"dewpointF": 59,
"maxHumidity": 76,
"minHumidity": 32,
"humidity": 76,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1014,
"pressureIN": 29.94,
"windDir": "NNE",
"windDirDEG": 21,
"windSpeedKTS": 1,
"windSpeedKPH": 2,
"windSpeedMPH": 1,
"windGustKTS": 17,
"windGustKPH": 32,
"windGustMPH": 20,
"windDirMax": "WNW",
"windDirMaxDEG": 301,
"windSpeedMaxKTS": 7,
"windSpeedMaxKPH": 13,
"windSpeedMaxMPH": 8,
"windDirMin": "NNE",
"windDirMinDEG": 21,
"windSpeedMinKTS": 1,
"windSpeedMinKPH": 2,
"windSpeedMinMPH": 1,
"windDir80m": "NW",
"windDir80mDEG": 309,
"windSpeed80mKTS": 5,
"windSpeed80mKPH": 9,
"windSpeed80mMPH": 5,
"windGust80mKTS": 17,
"windGust80mKPH": 31,
"windGust80mMPH": 19,
"windDirMax80m": "NW",
"windDirMax80mDEG": 322,
"windSpeedMax80mKTS": 17,
"windSpeedMax80mKPH": 31,
"windSpeedMax80mMPH": 19,
"windDirMin80m": "NW",
"windDirMin80mDEG": 309,
"windSpeedMin80mKTS": 5,
"windSpeedMin80mKPH": 9,
"windSpeedMin80mMPH": 5,
"sky": 47,
"cloudsCoded": "SC",
"weather": "Partly Cloudy",
"weatherCoded": [],
"weatherPrimary": "Partly Cloudy",
"weatherPrimaryCoded": "::SC",
"icon": "pcloudy.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": 7,
"solradWM2": 4785,
"solradMinWM2": 0,
"solradMaxWM2": 682,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665201146,
"sunset": 1665243161,
"sunriseISO": "2022-10-08T05:52:26+02:00",
"sunsetISO": "2022-10-08T17:32:41+02:00"
},
{
"timestamp": 1665291600,
"validTime": "2022-10-09T07:00:00+02:00",
"dateTimeISO": "2022-10-09T07:00:00+02:00",
"maxTempC": 31,
"maxTempF": 87,
"minTempC": 19,
"minTempF": 67,
"avgTempC": 25,
"avgTempF": 77,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 30,
"maxFeelslikeF": 86,
"minFeelslikeC": 20,
"minFeelslikeF": 67,
"avgFeelslikeC": 25,
"avgFeelslikeF": 77,
"feelslikeC": 20,
"feelslikeF": 67,
"maxDewpointC": 17,
"maxDewpointF": 63,
"minDewpointC": 11,
"minDewpointF": 52,
"avgDewpointC": 14,
"avgDewpointF": 57,
"dewpointC": 17,
"dewpointF": 63,
"maxHumidity": 86,
"minHumidity": 31,
"humidity": 86,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1016,
"pressureIN": 29.99,
"windDir": "N",
"windDirDEG": 356,
"windSpeedKTS": 2,
"windSpeedKPH": 4,
"windSpeedMPH": 2,
"windGustKTS": 19,
"windGustKPH": 36,
"windGustMPH": 22,
"windDirMax": "NNW",
"windDirMaxDEG": 343,
"windSpeedMaxKTS": 8,
"windSpeedMaxKPH": 14,
"windSpeedMaxMPH": 9,
"windDirMin": "N",
"windDirMinDEG": 356,
"windSpeedMinKTS": 2,
"windSpeedMinKPH": 4,
"windSpeedMinMPH": 2,
"windDir80m": "NW",
"windDir80mDEG": 316,
"windSpeed80mKTS": 5,
"windSpeed80mKPH": 9,
"windSpeed80mMPH": 6,
"windGust80mKTS": 20,
"windGust80mKPH": 36,
"windGust80mMPH": 23,
"windDirMax80m": "N",
"windDirMax80mDEG": 354,
"windSpeedMax80mKTS": 20,
"windSpeedMax80mKPH": 36,
"windSpeedMax80mMPH": 23,
"windDirMin80m": "NW",
"windDirMin80mDEG": 316,
"windSpeedMin80mKTS": 5,
"windSpeedMin80mKPH": 9,
"windSpeedMin80mMPH": 6,
"sky": 47,
"cloudsCoded": "SC",
"weather": "Partly Cloudy",
"weatherCoded": [],
"weatherPrimary": "Partly Cloudy",
"weatherPrimaryCoded": "::SC",
"icon": "pcloudy.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": 7,
"solradWM2": 4768,
"solradMinWM2": 0,
"solradMaxWM2": 726,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665287583,
"sunset": 1665329491,
"sunriseISO": "2022-10-09T05:53:03+02:00",
"sunsetISO": "2022-10-09T17:31:31+02:00"
},
{
"timestamp": 1665378000,
"validTime": "2022-10-10T07:00:00+02:00",
"dateTimeISO": "2022-10-10T07:00:00+02:00",
"maxTempC": 31,
"maxTempF": 87,
"minTempC": 21,
"minTempF": 70,
"avgTempC": 26,
"avgTempF": 78,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 30,
"maxFeelslikeF": 86,
"minFeelslikeC": 21,
"minFeelslikeF": 69,
"avgFeelslikeC": 25,
"avgFeelslikeF": 78,
"feelslikeC": 21,
"feelslikeF": 69,
"maxDewpointC": 16,
"maxDewpointF": 61,
"minDewpointC": 13,
"minDewpointF": 55,
"avgDewpointC": 14,
"avgDewpointF": 58,
"dewpointC": 16,
"dewpointF": 61,
"maxHumidity": 75,
"minHumidity": 35,
"humidity": 75,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1017,
"pressureIN": 30.03,
"windDir": "N",
"windDirDEG": 358,
"windSpeedKTS": 2,
"windSpeedKPH": 4,
"windSpeedMPH": 2,
"windGustKTS": 16,
"windGustKPH": 30,
"windGustMPH": 19,
"windDirMax": "N",
"windDirMaxDEG": 10,
"windSpeedMaxKTS": 8,
"windSpeedMaxKPH": 15,
"windSpeedMaxMPH": 9,
"windDirMin": "N",
"windDirMinDEG": 358,
"windSpeedMinKTS": 2,
"windSpeedMinKPH": 4,
"windSpeedMinMPH": 2,
"windDir80m": "N",
"windDir80mDEG": 8,
"windSpeed80mKTS": 7,
"windSpeed80mKPH": 13,
"windSpeed80mMPH": 8,
"windGust80mKTS": 19,
"windGust80mKPH": 36,
"windGust80mMPH": 22,
"windDirMax80m": "N",
"windDirMax80mDEG": 10,
"windSpeedMax80mKTS": 19,
"windSpeedMax80mKPH": 36,
"windSpeedMax80mMPH": 22,
"windDirMin80m": "E",
"windDirMin80mDEG": 91,
"windSpeedMin80mKTS": 7,
"windSpeedMin80mKPH": 13,
"windSpeedMin80mMPH": 8,
"sky": 64,
"cloudsCoded": "SC",
"weather": "Partly Cloudy",
"weatherCoded": [],
"weatherPrimary": "Partly Cloudy",
"weatherPrimaryCoded": "::SC",
"icon": "pcloudy.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": 6,
"solradWM2": 4494,
"solradMinWM2": 0,
"solradMaxWM2": 597,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665374020,
"sunset": 1665415821,
"sunriseISO": "2022-10-10T05:53:40+02:00",
"sunsetISO": "2022-10-10T17:30:21+02:00"
},
{
"timestamp": 1665464400,
"validTime": "2022-10-11T07:00:00+02:00",
"dateTimeISO": "2022-10-11T07:00:00+02:00",
"maxTempC": 31,
"maxTempF": 87,
"minTempC": 21,
"minTempF": 70,
"avgTempC": 26,
"avgTempF": 78,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 31,
"maxFeelslikeF": 87,
"minFeelslikeC": 22,
"minFeelslikeF": 72,
"avgFeelslikeC": 26,
"avgFeelslikeF": 79,
"feelslikeC": 22,
"feelslikeF": 72,
"maxDewpointC": 17,
"maxDewpointF": 62,
"minDewpointC": 11,
"minDewpointF": 51,
"avgDewpointC": 13,
"avgDewpointF": 55,
"dewpointC": 17,
"dewpointF": 62,
"maxHumidity": 71,
"minHumidity": 30,
"humidity": 71,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1015,
"pressureIN": 29.98,
"windDir": "NNE",
"windDirDEG": 13,
"windSpeedKTS": 8,
"windSpeedKPH": 15,
"windSpeedMPH": 9,
"windGustKTS": 15,
"windGustKPH": 28,
"windGustMPH": 17,
"windDirMax": "NNE",
"windDirMaxDEG": 28,
"windSpeedMaxKTS": 15,
"windSpeedMaxKPH": 28,
"windSpeedMaxMPH": 18,
"windDirMin": "NNE",
"windDirMinDEG": 14,
"windSpeedMinKTS": 7,
"windSpeedMinKPH": 14,
"windSpeedMinMPH": 8,
"windDir80m": "NNE",
"windDir80mDEG": 16,
"windSpeed80mKTS": 10,
"windSpeed80mKPH": 19,
"windSpeed80mMPH": 12,
"windGust80mKTS": 17,
"windGust80mKPH": 31,
"windGust80mMPH": 19,
"windDirMax80m": "NNE",
"windDirMax80mDEG": 28,
"windSpeedMax80mKTS": 17,
"windSpeedMax80mKPH": 31,
"windSpeedMax80mMPH": 19,
"windDirMin80m": "NNE",
"windDirMin80mDEG": 13,
"windSpeedMin80mKTS": 9,
"windSpeedMin80mKPH": 18,
"windSpeedMin80mMPH": 11,
"sky": 0,
"cloudsCoded": "CL",
"weather": "Sunny",
"weatherCoded": [],
"weatherPrimary": "Sunny",
"weatherPrimaryCoded": "::CL",
"icon": "sunny.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": null,
"solradWM2": 5450,
"solradMinWM2": 0,
"solradMaxWM2": 758,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665460458,
"sunset": 1665502153,
"sunriseISO": "2022-10-11T05:54:18+02:00",
"sunsetISO": "2022-10-11T17:29:13+02:00"
},
{
"timestamp": 1665550800,
"validTime": "2022-10-12T07:00:00+02:00",
"dateTimeISO": "2022-10-12T07:00:00+02:00",
"maxTempC": 31,
"maxTempF": 88,
"minTempC": 21,
"minTempF": 69,
"avgTempC": 26,
"avgTempF": 79,
"tempC": null,
"tempF": null,
"maxFeelslikeC": 31,
"maxFeelslikeF": 88,
"minFeelslikeC": 22,
"minFeelslikeF": 72,
"avgFeelslikeC": 26,
"avgFeelslikeF": 80,
"feelslikeC": 22,
"feelslikeF": 72,
"maxDewpointC": 16,
"maxDewpointF": 60,
"minDewpointC": 11,
"minDewpointF": 51,
"avgDewpointC": 13,
"avgDewpointF": 55,
"dewpointC": 16,
"dewpointF": 60,
"maxHumidity": 68,
"minHumidity": 29,
"humidity": 68,
"pop": 0,
"precipMM": 0,
"precipIN": 0,
"iceaccum": null,
"iceaccumMM": null,
"iceaccumIN": null,
"snowCM": 0,
"snowIN": 0,
"pressureMB": 1014,
"pressureIN": 29.95,
"windDir": "NNE",
"windDirDEG": 12,
"windSpeedKTS": 8,
"windSpeedKPH": 15,
"windSpeedMPH": 9,
"windGustKTS": 15,
"windGustKPH": 28,
"windGustMPH": 17,
"windDirMax": "E",
"windDirMaxDEG": 96,
"windSpeedMaxKTS": 14,
"windSpeedMaxKPH": 26,
"windSpeedMaxMPH": 16,
"windDirMin": "NNE",
"windDirMinDEG": 12,
"windSpeedMinKTS": 7,
"windSpeedMinKPH": 13,
"windSpeedMinMPH": 8,
"windDir80m": "NNE",
"windDir80mDEG": 15,
"windSpeed80mKTS": 10,
"windSpeed80mKPH": 19,
"windSpeed80mMPH": 12,
"windGust80mKTS": 18,
"windGust80mKPH": 33,
"windGust80mMPH": 21,
"windDirMax80m": "E",
"windDirMax80mDEG": 96,
"windSpeedMax80mKTS": 18,
"windSpeedMax80mKPH": 33,
"windSpeedMax80mMPH": 21,
"windDirMin80m": "NNE",
"windDirMin80mDEG": 15,
"windSpeedMin80mKTS": 10,
"windSpeedMin80mKPH": 18,
"windSpeedMin80mMPH": 11,
"sky": 27,
"cloudsCoded": "FW",
"weather": "Mostly Sunny",
"weatherCoded": [],
"weatherPrimary": "Mostly Sunny",
"weatherPrimaryCoded": "::FW",
"icon": "fair.png",
"visibilityKM": 24.135,
"visibilityMI": 15,
"uvi": null,
"solradWM2": 4740,
"solradMinWM2": 0,
"solradMaxWM2": 743,
"isDay": true,
"maxCoverage": "",
"sunrise": 1665546895,
"sunset": 1665588484,
"sunriseISO": "2022-10-12T05:54:55+02:00",
"sunsetISO": "2022-10-12T17:28:04+02:00"
}
],
"profile": {
"tz": "Africa/Cairo",
"elevM": 23,
"elevFT": 75
}
}
]
}

View File

@@ -0,0 +1,691 @@
<success>true</success>
<response>
<loc>
<long>31.25</long>
<lat>30.063</lat>
</loc>
<profile>
<elevM>23</elevM>
<tz>Africa/Cairo</tz>
<elevFT>75</elevFT>
</profile>
<periods>
<dateTimeISO>2022-10-06T07:00:00+02:00</dateTimeISO>
<windDirMin80m>E</windDirMin80m>
<windDirMin80mDEG>95</windDirMin80mDEG>
<feelslikeC>21</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>10</windSpeedMaxMPH>
<windDirDEG>353</windDirDEG>
<windDir>N</windDir>
<sunriseISO>2022-10-06T05:51:14+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>9</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>66</minTempF>
<snowIN>0</snowIN>
<weather>Mostly Sunny</weather>
<sunsetISO>2022-10-06T17:35:02+02:00</sunsetISO>
<maxFeelslikeC>32</maxFeelslikeC>
<humidity>77</humidity>
<windDir80m>N</windDir80m>
<maxFeelslikeF>89</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>22</sky>
<windGust80mMPH>25</windGust80mMPH>
<windSpeedMax80mMPH>25</windSpeedMax80mMPH>
<weatherPrimary>Mostly Sunny</weatherPrimary>
<windGust80mKPH>41</windGust80mKPH>
<avgDewpointF>58</avgDewpointF>
<windSpeedMax80mKPH>41</windSpeedMax80mKPH>
<windGust80mKTS>22</windGust80mKTS>
<avgDewpointC>14</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>22</windSpeedMax80mKTS>
<windDirMinDEG>353</windDirMinDEG>
<windSpeedMaxKPH>16</windSpeedMaxKPH>
<windSpeedMin80mKTS>8</windSpeedMin80mKTS>
<feelslikeF>70</feelslikeF>
<validTime>2022-10-06T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>10</windSpeedMin80mMPH>
<solradMaxWM2>778</solradMaxWM2>
<avgTempC>25</avgTempC>
<windSpeedMin80mKPH>15</windSpeedMin80mKPH>
<weatherPrimaryCoded>::FW</weatherPrimaryCoded>
<sunrise>1665028274</sunrise>
<avgTempF>78</avgTempF>
<windDirMin>N</windDirMin>
<maxCoverage/>
<icon>fair.png</icon>
<minFeelslikeC>21</minFeelslikeC>
<dewpointC>17</dewpointC>
<cloudsCoded>FW</cloudsCoded>
<minFeelslikeF>70</minFeelslikeF>
<minHumidity>29</minHumidity>
<dewpointF>63</dewpointF>
<windSpeed80mKTS>12</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>NNW</windDirMax>
<windSpeed80mMPH>13</windSpeed80mMPH>
<windSpeed80mKPH>22</windSpeed80mKPH>
<windDir80mDEG>11</windDir80mDEG>
<maxTempC>32</maxTempC>
<pressureMB>1015</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665032400</timestamp>
<maxTempF>90</maxTempF>
<tempF>null</tempF>
<minDewpointC>11</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>1</windSpeedMinKTS>
<windDirMax80mDEG>343</windDirMax80mDEG>
<windGustKTS>21</windGustKTS>
<windSpeedMinKPH>2</windSpeedMinKPH>
<maxDewpointF>63</maxDewpointF>
<windSpeedMinMPH>1</windSpeedMinMPH>
<avgFeelslikeC>26</avgFeelslikeC>
<uvi>6</uvi>
<windDirMax80m>NNW</windDirMax80m>
<maxDewpointC>17</maxDewpointC>
<pressureIN>29.97</pressureIN>
<avgFeelslikeF>80</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>19</minTempC>
<minDewpointF>52</minDewpointF>
<windSpeedKTS>5</windSpeedKTS>
<sunset>1665070502</sunset>
<solradWM2>5608</solradWM2>
<windSpeedKPH>9</windSpeedKPH>
<windGustMPH>25</windGustMPH>
<maxHumidity>77</maxHumidity>
<windSpeedMPH>6</windSpeedMPH>
<windGustKPH>40</windGustKPH>
<windDirMaxDEG>342</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<periods>
<dateTimeISO>2022-10-07T07:00:00+02:00</dateTimeISO>
<windDirMin80m>NNW</windDirMin80m>
<windDirMin80mDEG>347</windDirMin80mDEG>
<feelslikeC>19</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>8</windSpeedMaxMPH>
<windDirDEG>325</windDirDEG>
<windDir>NW</windDir>
<sunriseISO>2022-10-07T05:51:50+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>7</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>66</minTempF>
<snowIN>0</snowIN>
<weather>Mostly Sunny</weather>
<sunsetISO>2022-10-07T17:33:51+02:00</sunsetISO>
<maxFeelslikeC>29</maxFeelslikeC>
<humidity>77</humidity>
<windDir80m>NNW</windDir80m>
<maxFeelslikeF>85</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>30</sky>
<windGust80mMPH>23</windGust80mMPH>
<windSpeedMax80mMPH>23</windSpeedMax80mMPH>
<weatherPrimary>Mostly Sunny</weatherPrimary>
<windGust80mKPH>37</windGust80mKPH>
<avgDewpointF>54</avgDewpointF>
<windSpeedMax80mKPH>37</windSpeedMax80mKPH>
<windGust80mKTS>20</windGust80mKTS>
<avgDewpointC>12</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>20</windSpeedMax80mKTS>
<windDirMinDEG>325</windDirMinDEG>
<windSpeedMaxKPH>13</windSpeedMaxKPH>
<windSpeedMin80mKTS>6</windSpeedMin80mKTS>
<feelslikeF>67</feelslikeF>
<validTime>2022-10-07T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>6</windSpeedMin80mMPH>
<solradMaxWM2>742</solradMaxWM2>
<avgTempC>24</avgTempC>
<windSpeedMin80mKPH>10</windSpeedMin80mKPH>
<weatherPrimaryCoded>::FW</weatherPrimaryCoded>
<sunrise>1665114710</sunrise>
<avgTempF>76</avgTempF>
<windDirMin>NW</windDirMin>
<maxCoverage/>
<icon>fair.png</icon>
<minFeelslikeC>19</minFeelslikeC>
<dewpointC>15</dewpointC>
<cloudsCoded>FW</cloudsCoded>
<minFeelslikeF>67</minFeelslikeF>
<minHumidity>30</minHumidity>
<dewpointF>60</dewpointF>
<windSpeed80mKTS>6</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>WNW</windDirMax>
<windSpeed80mMPH>6</windSpeed80mMPH>
<windSpeed80mKPH>10</windSpeed80mKPH>
<windDir80mDEG>347</windDir80mDEG>
<maxTempC>30</maxTempC>
<pressureMB>1014</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665118800</timestamp>
<maxTempF>86</maxTempF>
<tempF>null</tempF>
<minDewpointC>10</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>1</windSpeedMinKTS>
<windDirMax80mDEG>316</windDirMax80mDEG>
<windGustKTS>16</windGustKTS>
<windSpeedMinKPH>2</windSpeedMinKPH>
<maxDewpointF>60</maxDewpointF>
<windSpeedMinMPH>1</windSpeedMinMPH>
<avgFeelslikeC>24</avgFeelslikeC>
<uvi>6</uvi>
<windDirMax80m>NW</windDirMax80m>
<maxDewpointC>15</maxDewpointC>
<pressureIN>29.95</pressureIN>
<avgFeelslikeF>76</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>19</minTempC>
<minDewpointF>50</minDewpointF>
<windSpeedKTS>1</windSpeedKTS>
<sunset>1665156831</sunset>
<solradWM2>5486</solradWM2>
<windSpeedKPH>2</windSpeedKPH>
<windGustMPH>18</windGustMPH>
<maxHumidity>77</maxHumidity>
<windSpeedMPH>1</windSpeedMPH>
<windGustKPH>29</windGustKPH>
<windDirMaxDEG>298</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<periods>
<dateTimeISO>2022-10-08T07:00:00+02:00</dateTimeISO>
<windDirMin80m>NW</windDirMin80m>
<windDirMin80mDEG>309</windDirMin80mDEG>
<feelslikeC>19</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>8</windSpeedMaxMPH>
<windDirDEG>21</windDirDEG>
<windDir>NNE</windDir>
<sunriseISO>2022-10-08T05:52:26+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>7</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>66</minTempF>
<snowIN>0</snowIN>
<weather>Partly Cloudy</weather>
<sunsetISO>2022-10-08T17:32:41+02:00</sunsetISO>
<maxFeelslikeC>30</maxFeelslikeC>
<humidity>76</humidity>
<windDir80m>NW</windDir80m>
<maxFeelslikeF>86</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>47</sky>
<windGust80mMPH>19</windGust80mMPH>
<windSpeedMax80mMPH>19</windSpeedMax80mMPH>
<weatherPrimary>Partly Cloudy</weatherPrimary>
<windGust80mKPH>31</windGust80mKPH>
<avgDewpointF>56</avgDewpointF>
<windSpeedMax80mKPH>31</windSpeedMax80mKPH>
<windGust80mKTS>17</windGust80mKTS>
<avgDewpointC>13</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>17</windSpeedMax80mKTS>
<windDirMinDEG>21</windDirMinDEG>
<windSpeedMaxKPH>13</windSpeedMaxKPH>
<windSpeedMin80mKTS>5</windSpeedMin80mKTS>
<feelslikeF>67</feelslikeF>
<validTime>2022-10-08T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>5</windSpeedMin80mMPH>
<solradMaxWM2>682</solradMaxWM2>
<avgTempC>25</avgTempC>
<windSpeedMin80mKPH>9</windSpeedMin80mKPH>
<weatherPrimaryCoded>::SC</weatherPrimaryCoded>
<sunrise>1665201146</sunrise>
<avgTempF>76</avgTempF>
<windDirMin>NNE</windDirMin>
<maxCoverage/>
<icon>pcloudy.png</icon>
<minFeelslikeC>19</minFeelslikeC>
<dewpointC>15</dewpointC>
<cloudsCoded>SC</cloudsCoded>
<minFeelslikeF>67</minFeelslikeF>
<minHumidity>32</minHumidity>
<dewpointF>59</dewpointF>
<windSpeed80mKTS>5</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>WNW</windDirMax>
<windSpeed80mMPH>5</windSpeed80mMPH>
<windSpeed80mKPH>9</windSpeed80mKPH>
<windDir80mDEG>309</windDir80mDEG>
<maxTempC>30</maxTempC>
<pressureMB>1014</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665205200</timestamp>
<maxTempF>87</maxTempF>
<tempF>null</tempF>
<minDewpointC>11</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>1</windSpeedMinKTS>
<windDirMax80mDEG>322</windDirMax80mDEG>
<windGustKTS>17</windGustKTS>
<windSpeedMinKPH>2</windSpeedMinKPH>
<maxDewpointF>59</maxDewpointF>
<windSpeedMinMPH>1</windSpeedMinMPH>
<avgFeelslikeC>25</avgFeelslikeC>
<uvi>7</uvi>
<windDirMax80m>NW</windDirMax80m>
<maxDewpointC>15</maxDewpointC>
<pressureIN>29.94</pressureIN>
<avgFeelslikeF>76</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>19</minTempC>
<minDewpointF>52</minDewpointF>
<windSpeedKTS>1</windSpeedKTS>
<sunset>1665243161</sunset>
<solradWM2>4785</solradWM2>
<windSpeedKPH>2</windSpeedKPH>
<windGustMPH>20</windGustMPH>
<maxHumidity>76</maxHumidity>
<windSpeedMPH>1</windSpeedMPH>
<windGustKPH>32</windGustKPH>
<windDirMaxDEG>301</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<periods>
<dateTimeISO>2022-10-09T07:00:00+02:00</dateTimeISO>
<windDirMin80m>NW</windDirMin80m>
<windDirMin80mDEG>316</windDirMin80mDEG>
<feelslikeC>20</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>9</windSpeedMaxMPH>
<windDirDEG>356</windDirDEG>
<windDir>N</windDir>
<sunriseISO>2022-10-09T05:53:03+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>8</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>67</minTempF>
<snowIN>0</snowIN>
<weather>Partly Cloudy</weather>
<sunsetISO>2022-10-09T17:31:31+02:00</sunsetISO>
<maxFeelslikeC>30</maxFeelslikeC>
<humidity>86</humidity>
<windDir80m>NW</windDir80m>
<maxFeelslikeF>86</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>47</sky>
<windGust80mMPH>23</windGust80mMPH>
<windSpeedMax80mMPH>23</windSpeedMax80mMPH>
<weatherPrimary>Partly Cloudy</weatherPrimary>
<windGust80mKPH>36</windGust80mKPH>
<avgDewpointF>57</avgDewpointF>
<windSpeedMax80mKPH>36</windSpeedMax80mKPH>
<windGust80mKTS>20</windGust80mKTS>
<avgDewpointC>14</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>20</windSpeedMax80mKTS>
<windDirMinDEG>356</windDirMinDEG>
<windSpeedMaxKPH>14</windSpeedMaxKPH>
<windSpeedMin80mKTS>5</windSpeedMin80mKTS>
<feelslikeF>67</feelslikeF>
<validTime>2022-10-09T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>6</windSpeedMin80mMPH>
<solradMaxWM2>726</solradMaxWM2>
<avgTempC>25</avgTempC>
<windSpeedMin80mKPH>9</windSpeedMin80mKPH>
<weatherPrimaryCoded>::SC</weatherPrimaryCoded>
<sunrise>1665287583</sunrise>
<avgTempF>77</avgTempF>
<windDirMin>N</windDirMin>
<maxCoverage/>
<icon>pcloudy.png</icon>
<minFeelslikeC>20</minFeelslikeC>
<dewpointC>17</dewpointC>
<cloudsCoded>SC</cloudsCoded>
<minFeelslikeF>67</minFeelslikeF>
<minHumidity>31</minHumidity>
<dewpointF>63</dewpointF>
<windSpeed80mKTS>5</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>NNW</windDirMax>
<windSpeed80mMPH>6</windSpeed80mMPH>
<windSpeed80mKPH>9</windSpeed80mKPH>
<windDir80mDEG>316</windDir80mDEG>
<maxTempC>31</maxTempC>
<pressureMB>1016</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665291600</timestamp>
<maxTempF>87</maxTempF>
<tempF>null</tempF>
<minDewpointC>11</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>2</windSpeedMinKTS>
<windDirMax80mDEG>354</windDirMax80mDEG>
<windGustKTS>19</windGustKTS>
<windSpeedMinKPH>4</windSpeedMinKPH>
<maxDewpointF>63</maxDewpointF>
<windSpeedMinMPH>2</windSpeedMinMPH>
<avgFeelslikeC>25</avgFeelslikeC>
<uvi>7</uvi>
<windDirMax80m>N</windDirMax80m>
<maxDewpointC>17</maxDewpointC>
<pressureIN>29.99</pressureIN>
<avgFeelslikeF>77</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>19</minTempC>
<minDewpointF>52</minDewpointF>
<windSpeedKTS>2</windSpeedKTS>
<sunset>1665329491</sunset>
<solradWM2>4768</solradWM2>
<windSpeedKPH>4</windSpeedKPH>
<windGustMPH>22</windGustMPH>
<maxHumidity>86</maxHumidity>
<windSpeedMPH>2</windSpeedMPH>
<windGustKPH>36</windGustKPH>
<windDirMaxDEG>343</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<periods>
<dateTimeISO>2022-10-10T07:00:00+02:00</dateTimeISO>
<windDirMin80m>E</windDirMin80m>
<windDirMin80mDEG>91</windDirMin80mDEG>
<feelslikeC>21</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>9</windSpeedMaxMPH>
<windDirDEG>358</windDirDEG>
<windDir>N</windDir>
<sunriseISO>2022-10-10T05:53:40+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>8</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>70</minTempF>
<snowIN>0</snowIN>
<weather>Partly Cloudy</weather>
<sunsetISO>2022-10-10T17:30:21+02:00</sunsetISO>
<maxFeelslikeC>30</maxFeelslikeC>
<humidity>75</humidity>
<windDir80m>N</windDir80m>
<maxFeelslikeF>86</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>64</sky>
<windGust80mMPH>22</windGust80mMPH>
<windSpeedMax80mMPH>22</windSpeedMax80mMPH>
<weatherPrimary>Partly Cloudy</weatherPrimary>
<windGust80mKPH>36</windGust80mKPH>
<avgDewpointF>58</avgDewpointF>
<windSpeedMax80mKPH>36</windSpeedMax80mKPH>
<windGust80mKTS>19</windGust80mKTS>
<avgDewpointC>14</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>19</windSpeedMax80mKTS>
<windDirMinDEG>358</windDirMinDEG>
<windSpeedMaxKPH>15</windSpeedMaxKPH>
<windSpeedMin80mKTS>7</windSpeedMin80mKTS>
<feelslikeF>69</feelslikeF>
<validTime>2022-10-10T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>8</windSpeedMin80mMPH>
<solradMaxWM2>597</solradMaxWM2>
<avgTempC>26</avgTempC>
<windSpeedMin80mKPH>13</windSpeedMin80mKPH>
<weatherPrimaryCoded>::SC</weatherPrimaryCoded>
<sunrise>1665374020</sunrise>
<avgTempF>78</avgTempF>
<windDirMin>N</windDirMin>
<maxCoverage/>
<icon>pcloudy.png</icon>
<minFeelslikeC>21</minFeelslikeC>
<dewpointC>16</dewpointC>
<cloudsCoded>SC</cloudsCoded>
<minFeelslikeF>69</minFeelslikeF>
<minHumidity>35</minHumidity>
<dewpointF>61</dewpointF>
<windSpeed80mKTS>7</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>N</windDirMax>
<windSpeed80mMPH>8</windSpeed80mMPH>
<windSpeed80mKPH>13</windSpeed80mKPH>
<windDir80mDEG>8</windDir80mDEG>
<maxTempC>31</maxTempC>
<pressureMB>1017</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665378000</timestamp>
<maxTempF>87</maxTempF>
<tempF>null</tempF>
<minDewpointC>13</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>2</windSpeedMinKTS>
<windDirMax80mDEG>10</windDirMax80mDEG>
<windGustKTS>16</windGustKTS>
<windSpeedMinKPH>4</windSpeedMinKPH>
<maxDewpointF>61</maxDewpointF>
<windSpeedMinMPH>2</windSpeedMinMPH>
<avgFeelslikeC>25</avgFeelslikeC>
<uvi>6</uvi>
<windDirMax80m>N</windDirMax80m>
<maxDewpointC>16</maxDewpointC>
<pressureIN>30.03</pressureIN>
<avgFeelslikeF>78</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>21</minTempC>
<minDewpointF>55</minDewpointF>
<windSpeedKTS>2</windSpeedKTS>
<sunset>1665415821</sunset>
<solradWM2>4494</solradWM2>
<windSpeedKPH>4</windSpeedKPH>
<windGustMPH>19</windGustMPH>
<maxHumidity>75</maxHumidity>
<windSpeedMPH>2</windSpeedMPH>
<windGustKPH>30</windGustKPH>
<windDirMaxDEG>10</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<periods>
<dateTimeISO>2022-10-11T07:00:00+02:00</dateTimeISO>
<windDirMin80m>NNE</windDirMin80m>
<windDirMin80mDEG>13</windDirMin80mDEG>
<feelslikeC>22</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>18</windSpeedMaxMPH>
<windDirDEG>13</windDirDEG>
<windDir>NNE</windDir>
<sunriseISO>2022-10-11T05:54:18+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>15</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>70</minTempF>
<snowIN>0</snowIN>
<weather>Sunny</weather>
<sunsetISO>2022-10-11T17:29:13+02:00</sunsetISO>
<maxFeelslikeC>31</maxFeelslikeC>
<humidity>71</humidity>
<windDir80m>NNE</windDir80m>
<maxFeelslikeF>87</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>0</sky>
<windGust80mMPH>19</windGust80mMPH>
<windSpeedMax80mMPH>19</windSpeedMax80mMPH>
<weatherPrimary>Sunny</weatherPrimary>
<windGust80mKPH>31</windGust80mKPH>
<avgDewpointF>55</avgDewpointF>
<windSpeedMax80mKPH>31</windSpeedMax80mKPH>
<windGust80mKTS>17</windGust80mKTS>
<avgDewpointC>13</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>17</windSpeedMax80mKTS>
<windDirMinDEG>14</windDirMinDEG>
<windSpeedMaxKPH>28</windSpeedMaxKPH>
<windSpeedMin80mKTS>9</windSpeedMin80mKTS>
<feelslikeF>72</feelslikeF>
<validTime>2022-10-11T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>11</windSpeedMin80mMPH>
<solradMaxWM2>758</solradMaxWM2>
<avgTempC>26</avgTempC>
<windSpeedMin80mKPH>18</windSpeedMin80mKPH>
<weatherPrimaryCoded>::CL</weatherPrimaryCoded>
<sunrise>1665460458</sunrise>
<avgTempF>78</avgTempF>
<windDirMin>NNE</windDirMin>
<maxCoverage/>
<icon>sunny.png</icon>
<minFeelslikeC>22</minFeelslikeC>
<dewpointC>17</dewpointC>
<cloudsCoded>CL</cloudsCoded>
<minFeelslikeF>72</minFeelslikeF>
<minHumidity>30</minHumidity>
<dewpointF>62</dewpointF>
<windSpeed80mKTS>10</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>NNE</windDirMax>
<windSpeed80mMPH>12</windSpeed80mMPH>
<windSpeed80mKPH>19</windSpeed80mKPH>
<windDir80mDEG>16</windDir80mDEG>
<maxTempC>31</maxTempC>
<pressureMB>1015</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665464400</timestamp>
<maxTempF>87</maxTempF>
<tempF>null</tempF>
<minDewpointC>11</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>7</windSpeedMinKTS>
<windDirMax80mDEG>28</windDirMax80mDEG>
<windGustKTS>15</windGustKTS>
<windSpeedMinKPH>14</windSpeedMinKPH>
<maxDewpointF>62</maxDewpointF>
<windSpeedMinMPH>8</windSpeedMinMPH>
<avgFeelslikeC>26</avgFeelslikeC>
<uvi>null</uvi>
<windDirMax80m>NNE</windDirMax80m>
<maxDewpointC>17</maxDewpointC>
<pressureIN>29.98</pressureIN>
<avgFeelslikeF>79</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>21</minTempC>
<minDewpointF>51</minDewpointF>
<windSpeedKTS>8</windSpeedKTS>
<sunset>1665502153</sunset>
<solradWM2>5450</solradWM2>
<windSpeedKPH>15</windSpeedKPH>
<windGustMPH>17</windGustMPH>
<maxHumidity>71</maxHumidity>
<windSpeedMPH>9</windSpeedMPH>
<windGustKPH>28</windGustKPH>
<windDirMaxDEG>28</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<periods>
<dateTimeISO>2022-10-12T07:00:00+02:00</dateTimeISO>
<windDirMin80m>NNE</windDirMin80m>
<windDirMin80mDEG>15</windDirMin80mDEG>
<feelslikeC>22</feelslikeC>
<visibilityMI>15</visibilityMI>
<windSpeedMaxMPH>16</windSpeedMaxMPH>
<windDirDEG>12</windDirDEG>
<windDir>NNE</windDir>
<sunriseISO>2022-10-12T05:54:55+02:00</sunriseISO>
<iceaccumMM>null</iceaccumMM>
<windSpeedMaxKTS>14</windSpeedMaxKTS>
<iceaccumIN>null</iceaccumIN>
<minTempF>69</minTempF>
<snowIN>0</snowIN>
<weather>Mostly Sunny</weather>
<sunsetISO>2022-10-12T17:28:04+02:00</sunsetISO>
<maxFeelslikeC>31</maxFeelslikeC>
<humidity>68</humidity>
<windDir80m>NNE</windDir80m>
<maxFeelslikeF>88</maxFeelslikeF>
<precipMM>0</precipMM>
<sky>27</sky>
<windGust80mMPH>21</windGust80mMPH>
<windSpeedMax80mMPH>21</windSpeedMax80mMPH>
<weatherPrimary>Mostly Sunny</weatherPrimary>
<windGust80mKPH>33</windGust80mKPH>
<avgDewpointF>55</avgDewpointF>
<windSpeedMax80mKPH>33</windSpeedMax80mKPH>
<windGust80mKTS>18</windGust80mKTS>
<avgDewpointC>13</avgDewpointC>
<precipIN>0</precipIN>
<windSpeedMax80mKTS>18</windSpeedMax80mKTS>
<windDirMinDEG>12</windDirMinDEG>
<windSpeedMaxKPH>26</windSpeedMaxKPH>
<windSpeedMin80mKTS>10</windSpeedMin80mKTS>
<feelslikeF>72</feelslikeF>
<validTime>2022-10-12T07:00:00+02:00</validTime>
<windSpeedMin80mMPH>11</windSpeedMin80mMPH>
<solradMaxWM2>743</solradMaxWM2>
<avgTempC>26</avgTempC>
<windSpeedMin80mKPH>18</windSpeedMin80mKPH>
<weatherPrimaryCoded>::FW</weatherPrimaryCoded>
<sunrise>1665546895</sunrise>
<avgTempF>79</avgTempF>
<windDirMin>NNE</windDirMin>
<maxCoverage/>
<icon>fair.png</icon>
<minFeelslikeC>22</minFeelslikeC>
<dewpointC>16</dewpointC>
<cloudsCoded>FW</cloudsCoded>
<minFeelslikeF>72</minFeelslikeF>
<minHumidity>29</minHumidity>
<dewpointF>60</dewpointF>
<windSpeed80mKTS>10</windSpeed80mKTS>
<pop>0</pop>
<snowCM>0</snowCM>
<windDirMax>E</windDirMax>
<windSpeed80mMPH>12</windSpeed80mMPH>
<windSpeed80mKPH>19</windSpeed80mKPH>
<windDir80mDEG>15</windDir80mDEG>
<maxTempC>31</maxTempC>
<pressureMB>1014</pressureMB>
<visibilityKM>24.135</visibilityKM>
<timestamp>1665550800</timestamp>
<maxTempF>88</maxTempF>
<tempF>null</tempF>
<minDewpointC>11</minDewpointC>
<solradMinWM2>0</solradMinWM2>
<windSpeedMinKTS>7</windSpeedMinKTS>
<windDirMax80mDEG>96</windDirMax80mDEG>
<windGustKTS>15</windGustKTS>
<windSpeedMinKPH>13</windSpeedMinKPH>
<maxDewpointF>60</maxDewpointF>
<windSpeedMinMPH>8</windSpeedMinMPH>
<avgFeelslikeC>26</avgFeelslikeC>
<uvi>null</uvi>
<windDirMax80m>E</windDirMax80m>
<maxDewpointC>16</maxDewpointC>
<pressureIN>29.95</pressureIN>
<avgFeelslikeF>80</avgFeelslikeF>
<iceaccum>null</iceaccum>
<isDay>true</isDay>
<minTempC>21</minTempC>
<minDewpointF>51</minDewpointF>
<windSpeedKTS>8</windSpeedKTS>
<sunset>1665588484</sunset>
<solradWM2>4740</solradWM2>
<windSpeedKPH>15</windSpeedKPH>
<windGustMPH>17</windGustMPH>
<maxHumidity>68</maxHumidity>
<windSpeedMPH>9</windSpeedMPH>
<windGustKPH>28</windGustKPH>
<windDirMaxDEG>96</windDirMaxDEG>
<tempC>null</tempC>
</periods>
<interval>day</interval>
<place>
<country>eg</country>
<name>cairo</name>
<state>qh</state>
</place>
</response>
<error>null</error>