├── .gitignore ├── CHANGELOG ├── LICENSE ├── README ├── eclipseCleanup.xml ├── eclipseFormatter.xml ├── eclipsePreferences.epf ├── eclipseTemplates.xml ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── github │ │ └── cliftonlabs │ │ └── json_simple │ │ ├── JsonArray.java │ │ ├── JsonException.java │ │ ├── JsonKey.java │ │ ├── JsonObject.java │ │ ├── Jsonable.java │ │ ├── Jsoner.java │ │ ├── Yylex.java │ │ └── Yytoken.java └── lex │ └── jsonstrict.lex └── test └── java └── com └── github └── cliftonlabs └── json_simple ├── JsonArrayTest.java ├── JsonObjectTest.java ├── JsonerTest.java └── YylexTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | target 3 | pom.xml.releaseBackup 4 | release.properties 5 | .project 6 | .classpath 7 | .metadata 8 | .settings 9 | *.log 10 | *.java~ 11 | *.xml 12 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Version 4.0.1 (2022/03/*) 2 | * Jsoner no longer escapes forward slashes (/) when serializing or pretty printing. 3 | * Jsoner now adds a space ( ) after a colon (:) when pretty printing. 4 | 5 | Version 4.0.0 (2021/07/*) 6 | * Jflex plugin was upgraded from 1.4.3 to 1.8.2. This resulted in JsonException#getPosition() returning a long instead of an int. If you don't do position based JsonException handling this is backwards compatible with 3.* versions of this library. If you do position based JsonException handling, just change your position variables from int to long and your project will work as expected. 7 | * Jflex removed %table as a code generation option and has been replaced with its default behavior %pack. 8 | * Jsoner, JsonArray, and JsonObject now call methods that pass their writer. This avoids convenience calls that instantiate their own writer. Expect minor performance improvements for JSON with many fields. 9 | * Removed deprecated method Jsoner.prettyPrint(String, int). Use Jsoner.prettyPrint(Reader, Writer, String, String) or Jsoner.prettyPrint(String) instead. 10 | 11 | Version 3.1.1 (2020/02/*) 12 | * JsonObject now has put methods that call JsonKey#getKey() on the given JsonKey as a convenience. 13 | * JsonObject now has remove methods that call JsonKey#getKey() on the given JsonKey as a convenience. 14 | * There are no such convenience methods for other methods in the Map API so it is important to remember the JsonObject is a Map not a Map. 15 | * Note these changes were avoided in the past because it makes the API inconsistent. However, since they account for the vast majority of interactions with the API they do reduce verbosity of the code. They will also likely be a custom additions in many projects anyway and thus are included in the official release to maximize compatibility. 16 | 17 | Version 3.1.0 (2019/03/*) 18 | * Jsoner exposed its internal pretty print function to give the caller more control over how it indents and newlines. It also uses a Reader's input and pretty print to a Writer's output, such as for formatting a file. 19 | * Jsoner#prettyPrint(String, int) deprecated in favor of the exposed function. 20 | * Jsoner#prettyPrint(String) will be kept as a reasonable convenience method. 21 | * JsonArray now has add methods that can be chained for inline instantiation. 22 | * JsonObject now has put methods that can be chained for inline instantiation. 23 | * Javadocs now correctly refer to RFC 7159. 24 | 25 | Version 3.0.2 (2018/01/*) 26 | * Bug fix: Serialization of primitive arrays no longer starts at the 2nd index with test cases. 27 | * Bug fix: Character or primitive char is supported and with test cases. 28 | * Chris (cbojar) was added to the list of Authors. 29 | 30 | Version 3.0.1 (2018/01/*) 31 | * Corrected javadocs. 32 | 33 | Version 3.0.0 (2018/01/*) 34 | * Removed all deprecated code: allowing development on case insensitive file systems. 35 | * Refactored: code into a java package that matches the maven artifact group and id. 36 | * Refactored: DeserializationException to JsonException. 37 | 38 | Version 2.3.1 (2018/02/*) 39 | * Bug fix: from 3.0.2 applied as a patch to 2.3.0 for the 2.3.1 release. 40 | 41 | Version 2.3.0 (2017/10/*) 42 | * Deprecated the feature in Jsoner that serializes Enums to fully qualified strings, the Enums should implement Jsonable instead. 43 | * Deprecated getEnum and getEnumOrDefault methods in JsonArray and JsonObject. 44 | * Deprecated get___(String) and get___OrDefault(String, ___) in favor of get___(JsonKey) and get___OrDefault(JsonKey) respectively. 45 | * Enhancement: Added JsonKey interface to aid with code maintainability and convenience. 46 | * Enhancement: Jsoner can mint a JsonKey. 47 | * Enhancement: Jsoner deserialization no longer throws IOExceptions in favor of a DeserializationException with an IOEXCEPTION problem since the code to handle one is typically duplicated to handle the other. 48 | 49 | Version 2.2.0 (2017/10/*) 50 | * Bug fix: getEnum and getInteger and friends return null when the paired value is null to better match the java Map contract. 51 | * Enhancement: Jsoner can prettyPrint with tabs or spaces. 52 | 53 | Version 2.1.2 (2016/10/*) 54 | * Bug fix: Jsoner#prettyPrint(printable) no longer quotes colons. 55 | * Enhancement: for convenience a colon missing between a key value pair will officially continue to function as it has since the 1.* versions. Deserialization test cases have been updated. 56 | 57 | Version 2.1.1 (2016/09/*) 58 | * Separated the CHANGLOG and LICENSE from the README as they are recognized by tools like github. Most of the information in README is linked to on the project's website. 59 | * Enhancement: JsonArray and JsonObject convenience gets are more flexible on their expected value. Booleans now allow Booleans or Strings. Numbers now allow Numbers or Strings. Strings now allow Booleans, Numbers, or Strings. 60 | 61 | Version 2.1.0 (2016/09/*) 62 | * Bug fix: JsonObject#getDefaultByte(key, defaultValue) now properly returns a byte value instead of a float. 63 | * Enhancement: JsonObject has typed gets for each JSON value type. 64 | * Enhancement: JsonArray and JsonObject no longer return primitives. 65 | 66 | Version 2.0.0 (2016/09/*) 67 | * Davin Loegering was added to the list of Authors. 68 | * Consolidated the author list, change log, and license files from the base directory into the README.txt file. 69 | * Removed ant build file. 70 | * SCM section of the POM is updated with the github information since the svn repo urls were 404s. 71 | * POM now defines the source at 1.7 instead of 1.2, and is the only cause for the major version increment. The 2.0.0 release of this library is otherwise 100% backwards compatible with the older versions. 72 | * Minor code quality changes have been made to the old files of the project. 73 | * JFlex plugin now included in POM. 74 | * JFlex will produce a lexing class from all lex files in src/main/lex. 75 | * Javadocs are now produced when the jar goal is executed. 76 | * Moved lex files from doc/ to src/main/lex. 77 | * Deprecated the old json.lex in favor of jsonstrict.lex. 78 | * Deprecated ContentHandler and doesn't have a 2.0 equivalent. 79 | * Deprecated ContainerFactory and doesn't have a 2.0 equivalent. 80 | * Deprecated ItemList and doesn't have a 2.0 equivalent. 81 | * Deprecated JSONParse and JSONValue in favor of Jsoner. 82 | * Deprecated JSONStreamAware and JSONAware in favor of Jsonable. 83 | * Deprecated JSONObject in favor of JsonObject. 84 | * Deprecated JSONArray in favor of JsonArray. 85 | * Deprecated org.json.simple.parser.ParseException for org.json.simple.DeserializationException. 86 | * Deprecated org.json.simple.parser.Yytoken for org.json.simple.Yytoken. 87 | * Deprecated org.json.simple.parser.Yylex for org.json.simple.Yylex. 88 | * Tests for deprecated classes have been reorganized and updated to ensure backwards compatibility is maintained throughout the 2.x release lifetime. 89 | * Classes that have been deprecated still have shoddy javadocs but were updated to not produce errors and warnings during the build process. 90 | * Classes introduced in the 2.0 release have substantial javadocs to help projects heathily update ASAP. 91 | * The Jsonable interface allows others to define how their objects should be serialized in JSON. 92 | * DeserializationException has a new problem type for disallowed tokens. 93 | * DeserializationException now recommends recovery actions based on the problem that caused the DeserializationException in its message. All recovery scenarios are basically the same so DeserializationException is still the only json-simple exception class. 94 | * The new Yytoken types are renamed. 95 | * The new Yytoken is robustly constructed only allowing a null value when it is a null value in the DATUM tokens. 96 | * Jsoner can escape strings provided to it to help with implementing the Jsonable interface. 97 | * Jsoner can pretty print JSON strings provided to it for logging and basic display purposes. 98 | * Jsoner can serialize data defined in the RFC 4627 specification and objects that implement the Jsonable interface. If data could be serialized multiple ways the deepest Jsonable implementation in the heiarchy is preferred. Any defined Jsonable implementation will be preferred before falling back to a default serialization. 99 | * Jsoner can serialize an Enum that doesn't implement Jsonable. 100 | * Jsoner will deserialize any numerical value as a BigDecimal. 101 | * Jsoner can deserialize JsonArrays, JsonObjects, Strings, Numbers, Booleans, and null from strings provided to it. 102 | * Jsoner can deserialize a JsonArray and exception out if any other value would be returned. 103 | * Jsoner can deserialize a JsonObject and exception out if any other value would be returned. 104 | * Jsoner can deserialize multiple JsonArrays, JsonObjects, Strings, Numbers, Booleans, and nulls from a single string provided to it. 105 | * Jsoner deserialization (parsing) is thread safe. 106 | * JsonArray is based on ArrayList. So it won't produce code warnings and can be used to construct a more convenient Collection. 107 | * JsonArrays that are homogeneous can be cast and copied into a provided collection of the homogenous type. 108 | * JsonArray contains gets for each allowed data type in JSON and convenience methods for Collections, Enums, and Maps. Note that they will throw ClassCastExceptions in such cases since it is still indictitive of a programmer's error. 109 | * JsonObject is based on HashMap. 110 | * JsonObject contains getTypeOrDefault for each allowed data type in JSON and convenience methods for Collections, Enums, and Maps. Note that they will throw ClassCastExceptions in such cases since it is still indictitive of a programmer's error. 111 | 112 | Version 1.1.1 (2012/01/29) 113 | * Supports OSGi 114 | * Accepts a java.util.Map parameter in constructor of JSONObject 115 | 116 | Version 1.1 (2009/01/23) 117 | * Supports stoppable SAX-like content handler for streaming of JSON text 118 | * Added JSONStreamAware to support streaming JSON text 119 | * Added ContainerFactory to support creating arbitrary Map and List as JSON object and JSON array container during decoding 120 | * Supports any Map and List as JSON object and JSON array container during encoding 121 | * Added interface JSONAware 122 | * Added ParseException to get detail error report while parsing 123 | * Added escaping for Unicode characters that cause problems for browser JS eval 124 | 125 | Version 1.02 (2009/01/10) 126 | * Updated json.lex to improve performance of the lexer 127 | * Removed Rope.java and related junit test 128 | 129 | Version 1.01 (2008/08/26) 130 | * License changed to a more commerce friendly and clear one, Apache License 2.0 131 | * Use JFlex to generate a faster Yylex.java 132 | * Added Rope.java to get faster string operations 133 | * Separate test codes from source codes 134 | * Added ant build file build.xml 135 | 136 | Version 1.0 (2006/04/15) 137 | * Initial version 138 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Clifton Labs 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | For more information please visit: 2 | https://cliftonlabs.github.io/json-simple/ 3 | 4 | For the javadocs please visit: 5 | https://cliftonlabs.github.io/json-simple/target/apidocs/index.html 6 | 7 | [DEVELOPERS] 8 | Use settings similar to those included in: 9 | eclipseCleanup.xml 10 | eclipseFormatter.xml 11 | eclipsePreferences.epf 12 | elcipseTemplates.xml 13 | 14 | Code sorting should follow this priority with no preference on visibility: Types > Static Initializers > Initializers > Static Fields > Fields > Constructors > Static Methods > Methods. 15 | 16 | Generate json-simple project files for eclipse: 17 | mvn eclipse:eclipse 18 | 19 | Build the project: 20 | mvn compile 21 | 22 | Run unit tests: 23 | mvn test 24 | 25 | Build the project with javadocs and source: 26 | mvn package 27 | 28 | Make the signed artifacts available to other local projects: 29 | mvn clean install 30 | 31 | Guided deploy of project with signed artifacts: 32 | Automated source cleanup. 33 | Update CHANGELOG. 34 | Acquire release keys based on guidance from: https://central.sonatype.org/pages/working-with-pgp-signatures.html 35 | gpg2 --full-gen-key 36 | gpg2 --keyserver hkp://pool.sks-keyservers.net --send-keys KEYHASHTOSEND 37 | Ensure the settings.xml file is present in the maven repostiory. For example: ~/.m2/settings.xml should have the contents: 38 | 39 | 40 | 41 | ossrh 42 | account_name_with_group_id_control_in_maven_central 43 | plain_text_password_so_ensure_file_has_restricted_permissions_and_access 44 | 45 | 46 | 47 | 48 | mvn release:clean 49 | mvn release:prepare 50 | mvn release:perform 51 | mvn release:clean 52 | 53 | Update gh-pages: 54 | 1) switch to gh-pages 55 | 2) Delete target/ 56 | 57 | 3) switch to latest json-simple release tag 58 | 4) mvn clean install 59 | 60 | 5) switch back to gh-pages 61 | 6) git add target/ 62 | 63 | 7) Update the index.html version references. 64 | 8) commit and push to gh-pages. 65 | 66 | -------------------------------------------------------------------------------- /eclipseCleanup.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /eclipseFormatter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | -------------------------------------------------------------------------------- /eclipseTemplates.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.github.cliftonlabs 4 | json-simple 5 | jar 6 | JSON.simple 7 | Java 7+ toolkit to quickly develop RFC 4627 JSON compatible applications. 8 | https://cliftonlabs.github.io/json-simple/ 9 | 4.0.2-SNAPSHOT 10 | 11 | UTF-8 12 | 13 | 14 | 15 | 16 | The Apache Software License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | 19 | 20 | 21 | 22 | scm:git:git@github.com:cliftonlabs/json-simple.git 23 | scm:git:git@github.com:cliftonlabs/json-simple.git 24 | https://github.com/cliftonlabs/json-simple.git 25 | HEAD 26 | 27 | 28 | 29 | Davin Loegering 30 | https://github.com/loegering 31 | 32 | 33 | 34 | 35 | 36 | junit 37 | junit 38 | 4.13.2 39 | test 40 | 41 | 42 | 43 | 44 | 45 | ossrh 46 | https://oss.sonatype.org/content/repositories/snapshots 47 | 48 | 49 | ossrh 50 | https://oss.sonatype.org/service/local/staging/deploy/maven2 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-compiler-plugin 59 | 3.8.1 60 | 61 | 1.7 62 | 1.7 63 | 64 | 65 | 66 | 67 | de.jflex 68 | jflex-maven-plugin 69 | 1.8.2 70 | 71 | 72 | jlex 73 | 74 | generate 75 | 76 | 77 | src/main/java 78 | 79 | src/main/lex 80 | 81 | true 82 | 83 | 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-jar-plugin 90 | 3.2.0 91 | 92 | 93 | 94 | true 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-source-plugin 104 | 3.2.1 105 | 106 | 107 | attach-sources 108 | verify 109 | 110 | jar-no-fork 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-javadoc-plugin 119 | 3.3.0 120 | 121 | 122 | attach-javadocs 123 | 124 | jar 125 | 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-gpg-plugin 133 | 3.0.1 134 | 135 | 136 | sign-artifacts 137 | verify 138 | 139 | sign 140 | 141 | 142 | 143 | 144 | 145 | 146 | org.sonatype.plugins 147 | nexus-staging-maven-plugin 148 | 1.6.8 149 | true 150 | 151 | ossrh 152 | https://oss.sonatype.org/ 153 | true 154 | deploy 155 | 156 | 157 | 158 | 159 | org.apache.maven.plugins 160 | maven-release-plugin 161 | 3.0.0-M4 162 | 163 | true 164 | true 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/JsonArray.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 Clifton Labs 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. */ 11 | package com.github.cliftonlabs.json_simple; 12 | 13 | import java.io.IOException; 14 | import java.io.StringWriter; 15 | import java.io.Writer; 16 | import java.math.BigDecimal; 17 | import java.util.ArrayList; 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.Map; 21 | 22 | /** JsonArray is a common non-thread safe data format for a collection of data. The contents of a JsonArray are only 23 | * validated as JSON values on serialization. Meaning all values added to a JsonArray must be recognized by the Jsoner 24 | * for it to be a true 'JsonArray', so it is really a JsonableArrayList that will serialize to a JsonArray if all of 25 | * its contents are valid JSON. 26 | * @see Jsoner 27 | * @since 2.0.0 */ 28 | public class JsonArray extends ArrayList implements Jsonable{ 29 | /** The serialization version this class is compatible with. This value doesn't need to be incremented if and only 30 | * if the only changes to occur were updating comments, updating javadocs, adding new fields to the class, changing 31 | * the fields from static to non-static, or changing the fields from transient to non transient. All other changes 32 | * require this number be incremented. */ 33 | private static final long serialVersionUID = 1L; 34 | 35 | /** Instantiates an empty JsonArray. */ 36 | public JsonArray(){ 37 | super(); 38 | } 39 | 40 | /** Instantiate a new JsonArray using ArrayList's constructor of the same type. 41 | * @param collection represents the elements to produce the JsonArray with. */ 42 | public JsonArray(final Collection collection){ 43 | super(collection); 44 | } 45 | 46 | /** Calls add for the given collection of elements, but returns the JsonArray for chaining calls. 47 | * @param collection represents the items to be appended to the JsonArray. 48 | * @return the JsonArray to allow chaining calls. 49 | * @see ArrayList#addAll(Collection) 50 | * @since 3.1.0 for inline instantiation. */ 51 | public JsonArray addAllChain(final Collection collection){ 52 | this.addAll(collection); 53 | return this; 54 | } 55 | 56 | /** Calls add for the given index and collection, but returns the JsonArray for chaining calls. 57 | * @param index represents what index the element is added to in the JsonArray. 58 | * @param collection represents the item to be appended to the JsonArray. 59 | * @return the JsonArray to allow chaining calls. 60 | * @see ArrayList#addAll(int, Collection) 61 | * @since 3.1.0 for inline instantiation. */ 62 | public JsonArray addAllChain(final int index, final Collection collection){ 63 | this.addAll(index, collection); 64 | return this; 65 | } 66 | 67 | /** Calls add for the given element, but returns the JsonArray for chaining calls. 68 | * @param index represents what index the element is added to in the JsonArray. 69 | * @param element represents the item to be appended to the JsonArray. 70 | * @return the JsonArray to allow chaining calls. 71 | * @see ArrayList#add(int, Object) 72 | * @since 3.1.0 for inline instantiation. */ 73 | public JsonArray addChain(final int index, final Object element){ 74 | this.add(index, element); 75 | return this; 76 | } 77 | 78 | /** Calls add for the given element, but returns the JsonArray for chaining calls. 79 | * @param element represents the item to be appended to the JsonArray. 80 | * @return the JsonArray to allow chaining calls. 81 | * @see ArrayList#add(Object) 82 | * @since 3.1.0 for inline instantiation. */ 83 | public JsonArray addChain(final Object element){ 84 | this.add(element); 85 | return this; 86 | } 87 | 88 | /** A convenience method that assumes every element of the JsonArray is castable to T before adding it to a 89 | * collection of Ts. 90 | * @param represents the type that all of the elements of the JsonArray should be cast to and the type the 91 | * collection will contain. 92 | * @param destination represents where all of the elements of the JsonArray are added to after being cast to the 93 | * generic type 94 | * provided. 95 | * @throws ClassCastException if the unchecked cast of an element to T fails. */ 96 | @SuppressWarnings("unchecked") 97 | public void asCollection(final Collection destination){ 98 | for(final Object o : this){ 99 | destination.add((T)o); 100 | } 101 | } 102 | 103 | /** A convenience method that assumes there is a BigDecimal, Number, or String at the given index. If a Number or 104 | * String is there it is used to construct a new BigDecimal. 105 | * @param index representing where the value is expected to be at. 106 | * @return the value stored at the key or the default provided if the key doesn't exist. 107 | * @throws ClassCastException if there was a value but didn't match the assumed return types. 108 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 109 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal. 110 | * @see BigDecimal 111 | * @see Number#doubleValue() */ 112 | public BigDecimal getBigDecimal(final int index){ 113 | Object returnable = this.get(index); 114 | if(returnable instanceof BigDecimal){ 115 | /* Success there was a BigDecimal. */ 116 | }else if(returnable instanceof Number){ 117 | /* A number can be used to construct a BigDecimal. */ 118 | returnable = new BigDecimal(returnable.toString()); 119 | }else if(returnable instanceof String){ 120 | /* A number can be used to construct a BigDecimal. */ 121 | returnable = new BigDecimal((String)returnable); 122 | } 123 | return (BigDecimal)returnable; 124 | } 125 | 126 | /** A convenience method that assumes there is a Boolean or String value at the given index. 127 | * @param index represents where the value is expected to be at. 128 | * @return the value at the index provided cast to a boolean. 129 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 130 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. */ 131 | public Boolean getBoolean(final int index){ 132 | Object returnable = this.get(index); 133 | if(returnable instanceof String){ 134 | returnable = Boolean.valueOf((String)returnable); 135 | } 136 | return (Boolean)returnable; 137 | } 138 | 139 | /** A convenience method that assumes there is a Number or String value at the given index. 140 | * @param index represents where the value is expected to be at. 141 | * @return the value at the index provided cast to a byte. 142 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 143 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 144 | * represents the double or float Infinity or NaN. 145 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 146 | * @see Number */ 147 | public Byte getByte(final int index){ 148 | Object returnable = this.get(index); 149 | if(returnable == null){ 150 | return null; 151 | } 152 | if(returnable instanceof String){ 153 | /* A String can be used to construct a BigDecimal. */ 154 | returnable = new BigDecimal((String)returnable); 155 | } 156 | return ((Number)returnable).byteValue(); 157 | } 158 | 159 | /** A convenience method that assumes there is a Collection value at the given index. 160 | * @param the kind of collection to expect at the index. Note unless manually added, collection values will be a 161 | * JsonArray. 162 | * @param index represents where the value is expected to be at. 163 | * @return the value at the index provided cast to a Collection. 164 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 165 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 166 | * @see Collection */ 167 | @SuppressWarnings("unchecked") 168 | public > T getCollection(final int index){ 169 | /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will 170 | * work. */ 171 | return (T)this.get(index); 172 | } 173 | 174 | /** A convenience method that assumes there is a Number or String value at the given index. 175 | * @param index represents where the value is expected to be at. 176 | * @return the value at the index provided cast to a double. 177 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 178 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 179 | * represents the double or float Infinity or NaN. 180 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 181 | * @see Number */ 182 | public Double getDouble(final int index){ 183 | Object returnable = this.get(index); 184 | if(returnable == null){ 185 | return null; 186 | } 187 | if(returnable instanceof String){ 188 | /* A String can be used to construct a BigDecimal. */ 189 | returnable = new BigDecimal((String)returnable); 190 | } 191 | return ((Number)returnable).doubleValue(); 192 | } 193 | 194 | /** A convenience method that assumes there is a Number or String value at the given index. 195 | * @param index represents where the value is expected to be at. 196 | * @return the value at the index provided cast to a float. 197 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 198 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 199 | * represents the double or float Infinity or NaN. 200 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 201 | * @see Number */ 202 | public Float getFloat(final int index){ 203 | Object returnable = this.get(index); 204 | if(returnable == null){ 205 | return null; 206 | } 207 | if(returnable instanceof String){ 208 | /* A String can be used to construct a BigDecimal. */ 209 | returnable = new BigDecimal((String)returnable); 210 | } 211 | return ((Number)returnable).floatValue(); 212 | } 213 | 214 | /** A convenience method that assumes there is a Number or String value at the given index. 215 | * @param index represents where the value is expected to be at. 216 | * @return the value at the index provided cast to a int. 217 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 218 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 219 | * represents the double or float Infinity or NaN. 220 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 221 | * @see Number */ 222 | public Integer getInteger(final int index){ 223 | Object returnable = this.get(index); 224 | if(returnable == null){ 225 | return null; 226 | } 227 | if(returnable instanceof String){ 228 | /* A String can be used to construct a BigDecimal. */ 229 | returnable = new BigDecimal((String)returnable); 230 | } 231 | return ((Number)returnable).intValue(); 232 | } 233 | 234 | /** A convenience method that assumes there is a Number or String value at the given index. 235 | * @param index represents where the value is expected to be at. 236 | * @return the value at the index provided cast to a long. 237 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 238 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 239 | * represents the double or float Infinity or NaN. 240 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 241 | * @see Number */ 242 | public Long getLong(final int index){ 243 | Object returnable = this.get(index); 244 | if(returnable == null){ 245 | return null; 246 | } 247 | if(returnable instanceof String){ 248 | /* A String can be used to construct a BigDecimal. */ 249 | returnable = new BigDecimal((String)returnable); 250 | } 251 | return ((Number)returnable).longValue(); 252 | } 253 | 254 | /** A convenience method that assumes there is a Map value at the given index. 255 | * @param the kind of map to expect at the index. Note unless manually added, Map values will be a JsonObject. 256 | * @param index represents where the value is expected to be at. 257 | * @return the value at the index provided cast to a Map. 258 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 259 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 260 | * @see Map */ 261 | @SuppressWarnings("unchecked") 262 | public > T getMap(final int index){ 263 | /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will 264 | * work. */ 265 | return (T)this.get(index); 266 | } 267 | 268 | /** A convenience method that assumes there is a Number or String value at the given index. 269 | * @param index represents where the value is expected to be at. 270 | * @return the value at the index provided cast to a short. 271 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 272 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 273 | * represents the double or float Infinity or NaN. 274 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. 275 | * @see Number */ 276 | public Short getShort(final int index){ 277 | Object returnable = this.get(index); 278 | if(returnable == null){ 279 | return null; 280 | } 281 | if(returnable instanceof String){ 282 | /* A String can be used to construct a BigDecimal. */ 283 | returnable = new BigDecimal((String)returnable); 284 | } 285 | return ((Number)returnable).shortValue(); 286 | } 287 | 288 | /** A convenience method that assumes there is a Boolean, Number, or String value at the given index. 289 | * @param index represents where the value is expected to be at. 290 | * @return the value at the index provided cast to a String. 291 | * @throws ClassCastException if there was a value but didn't match the assumed return type. 292 | * @throws IndexOutOfBoundsException if the index is outside of the range of element indexes in the JsonArray. */ 293 | public String getString(final int index){ 294 | Object returnable = this.get(index); 295 | if(returnable instanceof Boolean){ 296 | returnable = returnable.toString(); 297 | }else if(returnable instanceof Number){ 298 | returnable = returnable.toString(); 299 | } 300 | return (String)returnable; 301 | } 302 | 303 | /* (non-Javadoc) 304 | * @see org.json.simple.Jsonable#toJson() */ 305 | @Override 306 | public String toJson(){ 307 | final StringWriter writable = new StringWriter(); 308 | try{ 309 | this.toJson(writable); 310 | }catch(final IOException caught){ 311 | /* See java.io.StringWriter. */ 312 | } 313 | return writable.toString(); 314 | } 315 | 316 | /* (non-Javadoc) 317 | * @see org.json.simple.Jsonable#toJson(java.io.Writer) */ 318 | @Override 319 | public void toJson(final Writer writable) throws IOException{ 320 | boolean isFirstElement = true; 321 | final Iterator elements = this.iterator(); 322 | writable.write('['); 323 | while(elements.hasNext()){ 324 | if(isFirstElement){ 325 | isFirstElement = false; 326 | }else{ 327 | writable.write(','); 328 | } 329 | Jsoner.serialize(elements.next(), writable); 330 | } 331 | writable.write(']'); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/JsonException.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 Clifton Labs 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. */ 11 | package com.github.cliftonlabs.json_simple; 12 | 13 | /** JsonException explains how and where the problem occurs in the source JSON text during deserialization. 14 | * @since 3.0.0 */ 15 | public class JsonException extends Exception{ 16 | /** The kinds of exceptions that can trigger a JsonException. */ 17 | public enum Problems{ 18 | @SuppressWarnings("javadoc") 19 | DISALLOWED_TOKEN, 20 | /** @since 2.3.0 to consolidate exceptions that occur during deserialization. */ 21 | IOEXCEPTION, 22 | @SuppressWarnings("javadoc") 23 | UNEXPECTED_CHARACTER, 24 | @SuppressWarnings("javadoc") 25 | UNEXPECTED_EXCEPTION, 26 | @SuppressWarnings("javadoc") 27 | UNEXPECTED_TOKEN; 28 | } 29 | 30 | @SuppressWarnings("javadoc") 31 | private static final long serialVersionUID = 1L; 32 | @SuppressWarnings("javadoc") 33 | private final long position; 34 | @SuppressWarnings("javadoc") 35 | private final Problems problemType; 36 | @SuppressWarnings("javadoc") 37 | private final Object unexpectedObject; 38 | 39 | /** Instantiates a JsonException without assumptions. 40 | * @param position where the exception occurred. 41 | * @param problemType how the exception occurred. 42 | * @param unexpectedObject what caused the exception. */ 43 | public JsonException(final long position, final Problems problemType, final Object unexpectedObject){ 44 | this.position = position; 45 | this.problemType = problemType; 46 | this.unexpectedObject = unexpectedObject; 47 | if(Problems.IOEXCEPTION.equals(problemType) || Problems.UNEXPECTED_EXCEPTION.equals(problemType)){ 48 | if(unexpectedObject instanceof Throwable){ 49 | this.initCause((Throwable)unexpectedObject); 50 | } 51 | } 52 | } 53 | 54 | @Override 55 | public String getMessage(){ 56 | final StringBuilder sb = new StringBuilder(); 57 | switch(this.problemType){ 58 | case DISALLOWED_TOKEN: 59 | sb.append("The disallowed token (").append(this.unexpectedObject).append(") was found at position ").append(this.position).append(". If this is in error, try again with a deserialization method in Jsoner that allows the token instead. Otherwise, fix the parsable string and try again."); 60 | break; 61 | case IOEXCEPTION: 62 | sb.append("An IOException was encountered, ensure the reader is properly instantiated, isn't closed, or that it is ready before trying again.\n").append(this.unexpectedObject); 63 | break; 64 | case UNEXPECTED_CHARACTER: 65 | sb.append("The unexpected character (").append(this.unexpectedObject).append(") was found at position ").append(this.position).append(". Fix the parsable string and try again."); 66 | break; 67 | case UNEXPECTED_TOKEN: 68 | sb.append("The unexpected token ").append(this.unexpectedObject).append(" was found at position ").append(this.position).append(". Fix the parsable string and try again."); 69 | break; 70 | case UNEXPECTED_EXCEPTION: 71 | sb.append("Please report this to the library's maintainer. The unexpected exception that should be addressed before trying again occurred at position ").append(this.position).append(":\n").append(this.unexpectedObject); 72 | break; 73 | default: 74 | sb.append("Please report this to the library's maintainer. An error at position ").append(this.position).append(" occurred. There are no recovery recommendations available."); 75 | break; 76 | } 77 | return sb.toString(); 78 | } 79 | 80 | /** Helps debug the location of a problem. 81 | * @return an index of the string character the error type occurred at. */ 82 | public long getPosition(){ 83 | return this.position; 84 | } 85 | 86 | /** Helps find an appropriate solution for a problem. 87 | * @return the enumeration for how the exception occurred. */ 88 | public Problems getProblemType(){ 89 | return this.problemType; 90 | } 91 | 92 | /** Helps identify the problem. 93 | * @return a representation of what caused the exception. */ 94 | public Object getUnexpectedObject(){ 95 | return this.unexpectedObject; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/JsonKey.java: -------------------------------------------------------------------------------- 1 | package com.github.cliftonlabs.json_simple; 2 | 3 | /** Represents the key of a JsonObject. Utilizing JsonKeys allows many of the convenience methods that self document 4 | * your JSON data model and make refactoring easier. It is recommended to implement JsonKeys as an enum. 5 | * @since 2.3.0 */ 6 | public interface JsonKey{ 7 | /** The json-simple library uses a String for its keys. 8 | * @return a String representing the JsonKey. */ 9 | public String getKey(); 10 | 11 | /** A reasonable value for the key; such as a valid default, error value, or null. 12 | * @return an Object representing a reasonable general case value for the key. */ 13 | public Object getValue(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/JsonObject.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016-2017 Clifton Labs 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. */ 11 | package com.github.cliftonlabs.json_simple; 12 | 13 | import java.io.IOException; 14 | import java.io.StringWriter; 15 | import java.io.Writer; 16 | import java.math.BigDecimal; 17 | import java.util.Collection; 18 | import java.util.HashMap; 19 | import java.util.HashSet; 20 | import java.util.Iterator; 21 | import java.util.Map; 22 | import java.util.NoSuchElementException; 23 | import java.util.Set; 24 | 25 | /** JsonObject is a common non-thread safe data format for string to data mappings. The contents of a JsonObject are 26 | * only validated as JSON values on serialization. Meaning all values added to a JsonObject must be recognized by the 27 | * Jsoner for it to be a true 'JsonObject', so it is really a JsonableHashMap that will serialize to a JsonObject if all 28 | * of its contents are valid JSON. 29 | * @see Jsoner 30 | * @since 2.0.0 */ 31 | public class JsonObject extends HashMap implements Jsonable{ 32 | /** The serialization version this class is compatible with. This value doesn't need to be incremented if and only 33 | * if the only changes to occur were updating comments, updating javadocs, adding new fields to the class, changing 34 | * the fields from static to non-static, or changing the fields from transient to non transient. All other changes 35 | * require this number be incremented. */ 36 | private static final long serialVersionUID = 2L; 37 | 38 | /** Instantiates an empty JsonObject. */ 39 | public JsonObject(){ 40 | super(); 41 | } 42 | 43 | /** Instantiate a new JsonObject by accepting a map's entries, which could lead to de/serialization issues of the 44 | * resulting JsonObject since the entry values aren't validated as JSON values. 45 | * @param map represents the mappings to produce the JsonObject with. */ 46 | public JsonObject(final Map map){ 47 | super(map); 48 | } 49 | 50 | /** A convenience method that assumes there is a BigDecimal, Number, or String at the given key. If a Number is 51 | * there its Number#toString() is used to construct a new BigDecimal(String). If a String is there it is used to 52 | * construct a new BigDecimal(String). 53 | * @param key representing where the value ought to be paired with. 54 | * @return a BigDecimal representing the value paired with the key. 55 | * @throws ClassCastException if the value didn't match the assumed return type. 56 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 57 | * represents the double or float Infinity or NaN. 58 | * @see BigDecimal 59 | * @see Number#toString() 60 | * @see JsonKey 61 | * @since 2.3.0 to utilize JsonKey */ 62 | public BigDecimal getBigDecimal(final JsonKey key){ 63 | Object returnable = this.get(key.getKey()); 64 | if(returnable instanceof BigDecimal){ 65 | /* Success there was a BigDecimal or it defaulted. */ 66 | }else if(returnable instanceof Number){ 67 | /* A number can be used to construct a BigDecimal */ 68 | returnable = new BigDecimal(returnable.toString()); 69 | }else if(returnable instanceof String){ 70 | /* A number can be used to construct a BigDecimal */ 71 | returnable = new BigDecimal((String)returnable); 72 | } 73 | return (BigDecimal)returnable; 74 | } 75 | 76 | /** A convenience method that assumes there is a BigDecimal, Number, or String at the given key. If a Number is 77 | * there its Number#toString() is used to construct a new BigDecimal(String). If a String is there it is used to 78 | * construct a new BigDecimal(String). 79 | * @param key representing where the value ought to be paired with. 80 | * @return a BigDecimal representing the value paired with the key or JsonKey#getValue() if the key isn't present. 81 | * @throws ClassCastException if the value didn't match the assumed return type. 82 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 83 | * represents the double or float Infinity or NaN. 84 | * @see BigDecimal 85 | * @see Number#toString() 86 | * @see JsonKey 87 | * @since 2.3.0 to utilize JsonKey */ 88 | public BigDecimal getBigDecimalOrDefault(final JsonKey key){ 89 | Object returnable; 90 | if(this.containsKey(key.getKey())){ 91 | returnable = this.get(key.getKey()); 92 | }else{ 93 | returnable = key.getValue(); 94 | } 95 | if(returnable instanceof BigDecimal){ 96 | /* Success there was a BigDecimal or it defaulted. */ 97 | }else if(returnable instanceof Number){ 98 | /* A number can be used to construct a BigDecimal */ 99 | returnable = new BigDecimal(returnable.toString()); 100 | }else if(returnable instanceof String){ 101 | /* A String can be used to construct a BigDecimal */ 102 | returnable = new BigDecimal((String)returnable); 103 | } 104 | return (BigDecimal)returnable; 105 | } 106 | 107 | /** A convenience method that assumes there is a Boolean or String value at the given key. 108 | * @param key representing where the value ought to be paired with. 109 | * @return a Boolean representing the value paired with the key. 110 | * @throws ClassCastException if the value didn't match the assumed return type. 111 | * @see JsonKey 112 | * @since 2.3.0 to utilize JsonKey */ 113 | public Boolean getBoolean(final JsonKey key){ 114 | Object returnable = this.get(key.getKey()); 115 | if(returnable instanceof String){ 116 | returnable = Boolean.valueOf((String)returnable); 117 | } 118 | return (Boolean)returnable; 119 | } 120 | 121 | /** A convenience method that assumes there is a Boolean or String value at the given key. 122 | * @param key representing where the value ought to be paired with. 123 | * @return a Boolean representing the value paired with the key or JsonKey#getValue() if the key isn't present. 124 | * @throws ClassCastException if the value didn't match the assumed return type. 125 | * @see JsonKey 126 | * @since 2.3.0 to utilize JsonKey */ 127 | public Boolean getBooleanOrDefault(final JsonKey key){ 128 | Object returnable; 129 | if(this.containsKey(key.getKey())){ 130 | returnable = this.get(key.getKey()); 131 | }else{ 132 | returnable = key.getValue(); 133 | } 134 | if(returnable instanceof String){ 135 | returnable = Boolean.valueOf((String)returnable); 136 | } 137 | return (Boolean)returnable; 138 | } 139 | 140 | /** A convenience method that assumes there is a Number or String value at the given key. 141 | * @param key representing where the value ought to be paired with. 142 | * @return a Byte representing the value paired with the key (which may involve rounding or truncation). 143 | * @throws ClassCastException if the value didn't match the assumed return type. 144 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 145 | * represents the double or float Infinity or NaN. 146 | * @see Number#byteValue() 147 | * @see JsonKey 148 | * @since 2.3.0 to utilize JsonKey */ 149 | public Byte getByte(final JsonKey key){ 150 | Object returnable = this.get(key.getKey()); 151 | if(returnable == null){ 152 | return null; 153 | } 154 | if(returnable instanceof String){ 155 | /* A String can be used to construct a BigDecimal. */ 156 | returnable = new BigDecimal((String)returnable); 157 | } 158 | return ((Number)returnable).byteValue(); 159 | } 160 | 161 | /** A convenience method that assumes there is a Number or String value at the given key. 162 | * @param key representing where the value ought to be paired with. 163 | * @return a Byte representing the value paired with the key or JsonKey#getValue() if the key isn't present (which 164 | * may involve rounding or truncation). 165 | * @throws ClassCastException if the value didn't match the assumed return type. 166 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 167 | * represents the double or float Infinity or NaN. 168 | * @see Number#byteValue() 169 | * @see JsonKey 170 | * @since 2.3.0 to utilize JsonKey */ 171 | public Byte getByteOrDefault(final JsonKey key){ 172 | Object returnable; 173 | if(this.containsKey(key.getKey())){ 174 | returnable = this.get(key.getKey()); 175 | }else{ 176 | returnable = key.getValue(); 177 | } 178 | if(returnable == null){ 179 | return null; 180 | } 181 | if(returnable instanceof String){ 182 | /* A String can be used to construct a BigDecimal. */ 183 | returnable = new BigDecimal((String)returnable); 184 | } 185 | return ((Number)returnable).byteValue(); 186 | } 187 | 188 | /** A convenience method that assumes there is a Collection at the given key. 189 | * @param the kind of collection to expect at the key. Note unless manually added, collection values will be a 190 | * JsonArray. 191 | * @param key representing where the value ought to be paired with. 192 | * @return a Collection representing the value paired with the key. 193 | * @throws ClassCastException if the value didn't match the assumed return type. 194 | * @see JsonKey 195 | * @since 2.3.0 to utilize JsonKey */ 196 | @SuppressWarnings("unchecked") 197 | public > T getCollection(final JsonKey key){ 198 | /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will 199 | * work. */ 200 | return (T)this.get(key.getKey()); 201 | } 202 | 203 | /** A convenience method that assumes there is a Collection at the given key. 204 | * @param the kind of collection to expect at the key. Note unless manually added, collection values will be a 205 | * JsonArray. 206 | * @param key representing where the value ought to be paired with. 207 | * @return a Collection representing the value paired with the key or JsonKey#getValue() if the key isn't present.. 208 | * @throws ClassCastException if the value didn't match the assumed return type. 209 | * @see JsonKey 210 | * @since 2.3.0 to utilize JsonKey */ 211 | @SuppressWarnings("unchecked") 212 | public > T getCollectionOrDefault(final JsonKey key){ 213 | /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will 214 | * work. */ 215 | Object returnable; 216 | if(this.containsKey(key.getKey())){ 217 | returnable = this.get(key.getKey()); 218 | }else{ 219 | returnable = key.getValue(); 220 | } 221 | return (T)returnable; 222 | } 223 | 224 | /** A convenience method that assumes there is a Number or String value at the given key. 225 | * @param key representing where the value ought to be paired with. 226 | * @return a Double representing the value paired with the key (which may involve rounding or truncation). 227 | * @throws ClassCastException if the value didn't match the assumed return type. 228 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 229 | * represents the double or float Infinity or NaN. 230 | * @see Number#doubleValue() 231 | * @see JsonKey 232 | * @since 2.3.0 to utilize JsonKey */ 233 | public Double getDouble(final JsonKey key){ 234 | Object returnable = this.get(key.getKey()); 235 | if(returnable == null){ 236 | return null; 237 | } 238 | if(returnable instanceof String){ 239 | /* A String can be used to construct a BigDecimal. */ 240 | returnable = new BigDecimal((String)returnable); 241 | } 242 | return ((Number)returnable).doubleValue(); 243 | } 244 | 245 | /** A convenience method that assumes there is a Number or String value at the given key. 246 | * @param key representing where the value ought to be paired with. 247 | * @return a Double representing the value paired with the key or JsonKey#getValue() if the key isn't present (which 248 | * may involve rounding or truncation). 249 | * @throws ClassCastException if the value didn't match the assumed return type. 250 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 251 | * represents the double or float Infinity or NaN. 252 | * @see Number#doubleValue() 253 | * @see JsonKey 254 | * @since 2.3.0 to utilize JsonKey */ 255 | public Double getDoubleOrDefault(final JsonKey key){ 256 | Object returnable; 257 | if(this.containsKey(key.getKey())){ 258 | returnable = this.get(key.getKey()); 259 | }else{ 260 | returnable = key.getValue(); 261 | } 262 | if(returnable == null){ 263 | return null; 264 | } 265 | if(returnable instanceof String){ 266 | /* A String can be used to construct a BigDecimal. */ 267 | returnable = new BigDecimal((String)returnable); 268 | } 269 | return ((Number)returnable).doubleValue(); 270 | } 271 | 272 | /** A convenience method that assumes there is a Number or String value at the given key. 273 | * @param key representing where the value ought to be paired with. 274 | * @return a Float representing the value paired with the key (which may involve rounding or truncation). 275 | * @throws ClassCastException if the value didn't match the assumed return type. 276 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 277 | * represents the double or float Infinity or NaN. 278 | * @see Number#floatValue() 279 | * @see JsonKey 280 | * @since 2.3.0 to utilize JsonKey */ 281 | public Float getFloat(final JsonKey key){ 282 | Object returnable = this.get(key.getKey()); 283 | if(returnable == null){ 284 | return null; 285 | } 286 | if(returnable instanceof String){ 287 | /* A String can be used to construct a BigDecimal. */ 288 | returnable = new BigDecimal((String)returnable); 289 | } 290 | return ((Number)returnable).floatValue(); 291 | } 292 | 293 | /** A convenience method that assumes there is a Number or String value at the given key. 294 | * @param key representing where the value ought to be paired with. 295 | * @return a Float representing the value paired with the key or JsonKey#getValue() if the key isn't present (which 296 | * may involve rounding or truncation). 297 | * @throws ClassCastException if the value didn't match the assumed return type. 298 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 299 | * represents the double or float Infinity or NaN. 300 | * @see Number#floatValue() 301 | * @see JsonKey 302 | * @since 2.3.0 to utilize JsonKey */ 303 | public Float getFloatOrDefault(final JsonKey key){ 304 | Object returnable; 305 | if(this.containsKey(key.getKey())){ 306 | returnable = this.get(key.getKey()); 307 | }else{ 308 | returnable = key.getValue(); 309 | } 310 | if(returnable == null){ 311 | return null; 312 | } 313 | if(returnable instanceof String){ 314 | /* A String can be used to construct a BigDecimal. */ 315 | returnable = new BigDecimal((String)returnable); 316 | } 317 | return ((Number)returnable).floatValue(); 318 | } 319 | 320 | /** A convenience method that assumes there is a Number or String value at the given key. 321 | * @param key representing where the value ought to be paired with. 322 | * @return an Integer representing the value paired with the key (which may involve rounding or truncation). 323 | * @throws ClassCastException if the value didn't match the assumed return type. 324 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 325 | * represents the double or float Infinity or NaN. 326 | * @see Number#intValue() 327 | * @see JsonKey 328 | * @since 2.3.0 to utilize JsonKey */ 329 | public Integer getInteger(final JsonKey key){ 330 | Object returnable = this.get(key.getKey()); 331 | if(returnable == null){ 332 | return null; 333 | } 334 | if(returnable instanceof String){ 335 | /* A String can be used to construct a BigDecimal. */ 336 | returnable = new BigDecimal((String)returnable); 337 | } 338 | return ((Number)returnable).intValue(); 339 | } 340 | 341 | /** A convenience method that assumes there is a Number or String value at the given key. 342 | * @param key representing where the value ought to be paired with. 343 | * @return an Integer representing the value paired with the key or JsonKey#getValue() if the key isn't present 344 | * (which may involve rounding or truncation). 345 | * @throws ClassCastException if the value didn't match the assumed return type. 346 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 347 | * represents the double or float Infinity or NaN. 348 | * @see Number#intValue() 349 | * @see JsonKey 350 | * @since 2.3.0 to utilize JsonKey */ 351 | public Integer getIntegerOrDefault(final JsonKey key){ 352 | Object returnable; 353 | if(this.containsKey(key.getKey())){ 354 | returnable = this.get(key.getKey()); 355 | }else{ 356 | returnable = key.getValue(); 357 | } 358 | if(returnable == null){ 359 | return null; 360 | } 361 | if(returnable instanceof String){ 362 | /* A String can be used to construct a BigDecimal. */ 363 | returnable = new BigDecimal((String)returnable); 364 | } 365 | return ((Number)returnable).intValue(); 366 | } 367 | 368 | /** A convenience method that assumes there is a Number or String value at the given key. 369 | * @param key representing where the value ought to be paired with. 370 | * @return a Long representing the value paired with the key (which may involve rounding or truncation). 371 | * @throws ClassCastException if the value didn't match the assumed return type. 372 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 373 | * represents the double or float Infinity or NaN. 374 | * @see Number#longValue() 375 | * @see JsonKey 376 | * @since 2.3.0 to utilize JsonKey */ 377 | public Long getLong(final JsonKey key){ 378 | Object returnable = this.get(key.getKey()); 379 | if(returnable == null){ 380 | return null; 381 | } 382 | if(returnable instanceof String){ 383 | /* A String can be used to construct a BigDecimal. */ 384 | returnable = new BigDecimal((String)returnable); 385 | } 386 | return ((Number)returnable).longValue(); 387 | } 388 | 389 | /** A convenience method that assumes there is a Number or String value at the given key. 390 | * @param key representing where the value ought to be paired with. 391 | * @return a Long representing the value paired with the key or JsonKey#getValue() if the key isn't present (which 392 | * may involve rounding or truncation). 393 | * @throws ClassCastException if the value didn't match the assumed return type. 394 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 395 | * represents the double or float Infinity or NaN. 396 | * @see Number#longValue() 397 | * @see JsonKey 398 | * @since 2.3.0 to utilize JsonKey */ 399 | public Long getLongOrDefault(final JsonKey key){ 400 | Object returnable; 401 | if(this.containsKey(key.getKey())){ 402 | returnable = this.get(key.getKey()); 403 | }else{ 404 | returnable = key.getValue(); 405 | } 406 | if(returnable == null){ 407 | return null; 408 | } 409 | if(returnable instanceof String){ 410 | /* A String can be used to construct a BigDecimal. */ 411 | returnable = new BigDecimal((String)returnable); 412 | } 413 | return ((Number)returnable).longValue(); 414 | } 415 | 416 | /** A convenience method that assumes there is a Map at the given key. 417 | * @param the kind of map to expect at the key. Note unless manually added, Map values will be a JsonObject. 418 | * @param key representing where the value ought to be paired with. 419 | * @return a Map representing the value paired with the key. 420 | * @throws ClassCastException if the value didn't match the assumed return type. 421 | * @see JsonKey 422 | * @since 2.3.0 to utilize JsonKey */ 423 | @SuppressWarnings("unchecked") 424 | public > T getMap(final JsonKey key){ 425 | /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will 426 | * work. */ 427 | return (T)this.get(key.getKey()); 428 | } 429 | 430 | /** A convenience method that assumes there is a Map at the given key. 431 | * @param the kind of map to expect at the key. Note unless manually added, Map values will be a JsonObject. 432 | * @param key representing where the value ought to be paired with. 433 | * @return a Map representing the value paired with the key or JsonKey#getValue() if the key isn't present. 434 | * @throws ClassCastException if the value didn't match the assumed return type. 435 | * @see JsonKey 436 | * @since 2.3.0 to utilize JsonKey */ 437 | @SuppressWarnings("unchecked") 438 | public > T getMapOrDefault(final JsonKey key){ 439 | /* The unchecked warning is suppressed because there is no way of guaranteeing at compile time the cast will 440 | * work. */ 441 | Object returnable; 442 | if(this.containsKey(key.getKey())){ 443 | returnable = this.get(key.getKey()); 444 | }else{ 445 | returnable = key.getValue(); 446 | } 447 | return (T)returnable; 448 | } 449 | 450 | /** A convenience method that assumes there is a Number or String value at the given key. 451 | * @param key representing where the value ought to be paired with. 452 | * @return a Short representing the value paired with the key (which may involve rounding or truncation). 453 | * @throws ClassCastException if the value didn't match the assumed return type. 454 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 455 | * represents the double or float Infinity or NaN. 456 | * @see Number#shortValue() 457 | * @see JsonKey 458 | * @since 2.3.0 to utilize JsonKey */ 459 | public Short getShort(final JsonKey key){ 460 | Object returnable = this.get(key.getKey()); 461 | if(returnable == null){ 462 | return null; 463 | } 464 | if(returnable instanceof String){ 465 | /* A String can be used to construct a BigDecimal. */ 466 | returnable = new BigDecimal((String)returnable); 467 | } 468 | return ((Number)returnable).shortValue(); 469 | } 470 | 471 | /** A convenience method that assumes there is a Number or String value at the given key. 472 | * @param key representing where the value ought to be paired with. 473 | * @return a Short representing the value paired with the key or JsonKey#getValue() if the key isn't present (which 474 | * may involve rounding or truncation). 475 | * @throws ClassCastException if the value didn't match the assumed return type. 476 | * @throws NumberFormatException if a String isn't a valid representation of a BigDecimal or if the Number 477 | * represents the double or float Infinity or NaN. 478 | * @see Number#shortValue() 479 | * @see JsonKey 480 | * @since 2.3.0 to utilize JsonKey */ 481 | public Short getShortOrDefault(final JsonKey key){ 482 | Object returnable; 483 | if(this.containsKey(key.getKey())){ 484 | returnable = this.get(key.getKey()); 485 | }else{ 486 | returnable = key.getValue(); 487 | } 488 | if(returnable == null){ 489 | return null; 490 | } 491 | if(returnable instanceof String){ 492 | /* A String can be used to construct a BigDecimal. */ 493 | returnable = new BigDecimal((String)returnable); 494 | } 495 | return ((Number)returnable).shortValue(); 496 | } 497 | 498 | /** A convenience method that assumes there is a Boolean, Number, or String value at the given key. 499 | * @param key representing where the value ought to be paired with. 500 | * @return a String representing the value paired with the key. 501 | * @throws ClassCastException if the value didn't match the assumed return type. 502 | * @see JsonKey 503 | * @since 2.3.0 to utilize JsonKey */ 504 | public String getString(final JsonKey key){ 505 | Object returnable = this.get(key.getKey()); 506 | if(returnable instanceof Boolean){ 507 | returnable = returnable.toString(); 508 | }else if(returnable instanceof Number){ 509 | returnable = returnable.toString(); 510 | } 511 | return (String)returnable; 512 | } 513 | 514 | /** A convenience method that assumes there is a Boolean, Number, or String value at the given key. 515 | * @param key representing where the value ought to be paired with. 516 | * @return a String representing the value paired with the key or JsonKey#getValue() if the key isn't present. 517 | * @throws ClassCastException if the value didn't match the assumed return type. 518 | * @see JsonKey 519 | * @since 2.3.0 to utilize JsonKey */ 520 | public String getStringOrDefault(final JsonKey key){ 521 | Object returnable; 522 | if(this.containsKey(key.getKey())){ 523 | returnable = this.get(key.getKey()); 524 | }else{ 525 | returnable = key.getValue(); 526 | } 527 | if(returnable instanceof Boolean){ 528 | returnable = returnable.toString(); 529 | }else if(returnable instanceof Number){ 530 | returnable = returnable.toString(); 531 | } 532 | return (String)returnable; 533 | } 534 | 535 | /** Convenience method that calls put for the given key and value. 536 | * @param key represents the JsonKey used for the value's association in the map. 537 | * @param value represents the key's association in the map. 538 | * @see Map#put(Object, Object) 539 | * @since 3.1.1 to use JsonKey instead of calling JsonKey#getKey() each time. */ 540 | public void put(final JsonKey key, final Object value){ 541 | this.put(key.getKey(), value); 542 | } 543 | 544 | /** Calls putAll for the given map, but returns the JsonObject for chaining calls. 545 | * @param map represents the map to be copied into the JsonObject. 546 | * @return the JsonObject to allow chaining calls. 547 | * @see Map#putAll(Map) 548 | * @since 3.1.0 for inline instantiation. */ 549 | public JsonObject putAllChain(final Map map){ 550 | this.putAll(map); 551 | return this; 552 | } 553 | 554 | /** Convenience method that calls put for the given key and value, but returns the JsonObject for chaining calls. 555 | * @param key represents the JsonKey used for the value's association in the map. 556 | * @param value represents the key's association in the map. 557 | * @return the JsonObject to allow chaining calls. 558 | * @see Map#put(Object, Object) 559 | * @since 3.1.1 to use JsonKey instead of calling JsonKey#getKey() each time. */ 560 | public JsonObject putChain(final JsonKey key, final Object value){ 561 | this.put(key.getKey(), value); 562 | return this; 563 | } 564 | 565 | /** Calls put for the given key and value, but returns the JsonObject for chaining calls. 566 | * @param key represents the value's association in the map. 567 | * @param value represents the key's association in the map. 568 | * @return the JsonObject to allow chaining calls. 569 | * @see Map#put(Object, Object) 570 | * @since 3.1.0 for inline instantiation. */ 571 | public JsonObject putChain(final String key, final Object value){ 572 | this.put(key, value); 573 | return this; 574 | } 575 | 576 | /** Convenience method that calls remove for the given key. 577 | * @param key represents the value's association in the map. 578 | * @return an object representing the removed value or null if there wasn't one. 579 | * @since 3.1.1 to use JsonKey instead of calling JsonKey#getKey() each time. 580 | * @see Map#remove(Object) */ 581 | public Object remove(final JsonKey key){ 582 | return this.remove(key.getKey()); 583 | } 584 | 585 | /** Convenience method that calls remove for the given key and value. 586 | * @param key represents the value's association in the map. 587 | * @param value represents the expected value at the given key. 588 | * @return a boolean, which is true if the value was removed. It is false otherwise. 589 | * @since 3.1.1 to use JsonKey instead of calling JsonKey#getKey() each time. 590 | * @see Map#remove(Object, Object) */ 591 | public boolean remove(final JsonKey key, final Object value){ 592 | return this.remove(key.getKey(), value); 593 | } 594 | 595 | /** Ensures the given keys are present. 596 | * @param keys represents the keys that must be present. 597 | * @throws NoSuchElementException if any of the given keys are missing. 598 | * @since 2.3.0 to ensure critical keys are in the JsonObject. */ 599 | public void requireKeys(final JsonKey... keys){ 600 | /* Track all of the missing keys. */ 601 | final Set missing = new HashSet<>(); 602 | for(final JsonKey k : keys){ 603 | if(!this.containsKey(k.getKey())){ 604 | missing.add(k); 605 | } 606 | } 607 | if(!missing.isEmpty()){ 608 | /* Report any missing keys in the exception. */ 609 | final StringBuilder sb = new StringBuilder(); 610 | for(final JsonKey k : missing){ 611 | sb.append(k.getKey()).append(", "); 612 | } 613 | sb.setLength(sb.length() - 2); 614 | final String s = missing.size() > 1 ? "s" : ""; 615 | throw new NoSuchElementException("A JsonObject is missing required key" + s + ": " + sb.toString()); 616 | } 617 | } 618 | 619 | /* (non-Javadoc) 620 | * @see org.json.simple.Jsonable#toJson() */ 621 | @Override 622 | public String toJson(){ 623 | final StringWriter writable = new StringWriter(); 624 | try{ 625 | this.toJson(writable); 626 | }catch(final IOException caught){ 627 | /* See java.io.StringWriter. */ 628 | } 629 | return writable.toString(); 630 | } 631 | 632 | /* (non-Javadoc) 633 | * @see org.json.simple.Jsonable#toJson(java.io.Writer) */ 634 | @Override 635 | public void toJson(final Writer writable) throws IOException{ 636 | /* Writes the map in JSON object format. */ 637 | boolean isFirstEntry = true; 638 | final Iterator> entries = this.entrySet().iterator(); 639 | writable.write('{'); 640 | while(entries.hasNext()){ 641 | if(isFirstEntry){ 642 | isFirstEntry = false; 643 | }else{ 644 | writable.write(','); 645 | } 646 | final Map.Entry entry = entries.next(); 647 | Jsoner.serialize(entry.getKey(), writable); 648 | writable.write(':'); 649 | Jsoner.serialize(entry.getValue(), writable); 650 | } 651 | writable.write('}'); 652 | } 653 | } 654 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/Jsonable.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Clifton Labs 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. */ 11 | package com.github.cliftonlabs.json_simple; 12 | 13 | import java.io.IOException; 14 | import java.io.Writer; 15 | 16 | /** Jsonables can be serialized in java script object notation (JSON). Deserializing a String produced by a Jsonable 17 | * should represent the Jsonable in JSON form. 18 | * @since 2.0.0 */ 19 | public interface Jsonable{ 20 | /** Serialize to a JSON formatted string. 21 | * @return a string, formatted in JSON, that represents the Jsonable. */ 22 | public String toJson(); 23 | 24 | /** Serialize to a JSON formatted stream. 25 | * @param writable where the resulting JSON text should be sent. 26 | * @throws IOException when the writable encounters an I/O error. */ 27 | public void toJson(Writer writable) throws IOException; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/Yylex.java: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT 2 | // Generated by JFlex 1.8.2 http://jflex.de/ 3 | // source: src/main/lex/jsonstrict.lex 4 | 5 | package com.github.cliftonlabs.json_simple; 6 | 7 | 8 | // See https://github.com/jflex-de/jflex/issues/222 9 | @SuppressWarnings("FallThrough") 10 | class Yylex { 11 | 12 | /** This character denotes the end of file. */ 13 | public static final int YYEOF = -1; 14 | 15 | /** Initial size of the lookahead buffer. */ 16 | private static final int ZZ_BUFFERSIZE = 16384; 17 | 18 | // Lexical states. 19 | public static final int YYINITIAL = 0; 20 | public static final int STRING_BEGIN = 2; 21 | 22 | /** 23 | * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l 24 | * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l 25 | * at the beginning of a line 26 | * l is of the form l = 2*k, k a non negative integer 27 | */ 28 | private static final int ZZ_LEXSTATE[] = { 29 | 0, 0, 1, 1 30 | }; 31 | 32 | /** 33 | * Top-level table for translating characters to character classes 34 | */ 35 | private static final int [] ZZ_CMAP_TOP = zzUnpackcmap_top(); 36 | 37 | private static final String ZZ_CMAP_TOP_PACKED_0 = 38 | "\1\0\327\u0100\10\u0200\u1020\u0100"; 39 | 40 | private static int [] zzUnpackcmap_top() { 41 | int [] result = new int[4352]; 42 | int offset = 0; 43 | offset = zzUnpackcmap_top(ZZ_CMAP_TOP_PACKED_0, offset, result); 44 | return result; 45 | } 46 | 47 | private static int zzUnpackcmap_top(String packed, int offset, int [] result) { 48 | int i = 0; /* index in packed string */ 49 | int j = offset; /* index in unpacked array */ 50 | int l = packed.length(); 51 | while (i < l) { 52 | int count = packed.charAt(i++); 53 | int value = packed.charAt(i++); 54 | do result[j++] = value; while (--count > 0); 55 | } 56 | return j; 57 | } 58 | 59 | 60 | /** 61 | * Second-level tables for translating characters to character classes 62 | */ 63 | private static final int [] ZZ_CMAP_BLOCKS = zzUnpackcmap_blocks(); 64 | 65 | private static final String ZZ_CMAP_BLOCKS_PACKED_0 = 66 | "\11\0\2\1\2\0\1\1\22\0\1\1\1\0\1\2"+ 67 | "\10\0\1\3\1\4\1\5\1\6\1\7\12\10\1\11"+ 68 | "\6\0\4\12\1\13\1\12\24\0\1\14\1\15\1\16"+ 69 | "\3\0\1\17\1\20\2\12\1\21\1\22\5\0\1\23"+ 70 | "\1\0\1\24\3\0\1\25\1\26\1\27\1\30\5\0"+ 71 | "\1\31\1\0\1\32\u0182\0\u0100\33"; 72 | 73 | private static int [] zzUnpackcmap_blocks() { 74 | int [] result = new int[768]; 75 | int offset = 0; 76 | offset = zzUnpackcmap_blocks(ZZ_CMAP_BLOCKS_PACKED_0, offset, result); 77 | return result; 78 | } 79 | 80 | private static int zzUnpackcmap_blocks(String packed, int offset, int [] result) { 81 | int i = 0; /* index in packed string */ 82 | int j = offset; /* index in unpacked array */ 83 | int l = packed.length(); 84 | while (i < l) { 85 | int count = packed.charAt(i++); 86 | int value = packed.charAt(i++); 87 | do result[j++] = value; while (--count > 0); 88 | } 89 | return j; 90 | } 91 | 92 | /** 93 | * Translates DFA states to action switch labels. 94 | */ 95 | private static final int [] ZZ_ACTION = zzUnpackAction(); 96 | 97 | private static final String ZZ_ACTION_PACKED_0 = 98 | "\2\0\1\1\1\2\1\3\1\4\1\1\1\5\1\6"+ 99 | "\1\7\1\10\3\1\1\11\1\12\1\13\1\14\1\15"+ 100 | "\5\0\1\16\1\17\1\15\1\20\1\21\1\22\1\23"+ 101 | "\1\24\1\0\1\5\1\0\1\5\4\0\1\25\1\26"+ 102 | "\2\0\1\27"; 103 | 104 | private static int [] zzUnpackAction() { 105 | int [] result = new int[45]; 106 | int offset = 0; 107 | offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); 108 | return result; 109 | } 110 | 111 | private static int zzUnpackAction(String packed, int offset, int [] result) { 112 | int i = 0; /* index in packed string */ 113 | int j = offset; /* index in unpacked array */ 114 | int l = packed.length(); 115 | while (i < l) { 116 | int count = packed.charAt(i++); 117 | int value = packed.charAt(i++); 118 | do result[j++] = value; while (--count > 0); 119 | } 120 | return j; 121 | } 122 | 123 | 124 | /** 125 | * Translates a state to a row index in the transition table 126 | */ 127 | private static final int [] ZZ_ROWMAP = zzUnpackRowMap(); 128 | 129 | private static final String ZZ_ROWMAP_PACKED_0 = 130 | "\0\0\0\34\0\70\0\124\0\70\0\70\0\160\0\214"+ 131 | "\0\70\0\70\0\70\0\250\0\304\0\340\0\70\0\70"+ 132 | "\0\374\0\70\0\u0118\0\u0134\0\u0150\0\u016c\0\u0188\0\u01a4"+ 133 | "\0\70\0\70\0\70\0\70\0\70\0\70\0\70\0\70"+ 134 | "\0\u01c0\0\u01dc\0\u01f8\0\u01f8\0\u0214\0\u0230\0\u024c\0\u0268"+ 135 | "\0\70\0\70\0\u0284\0\u02a0\0\70"; 136 | 137 | private static int [] zzUnpackRowMap() { 138 | int [] result = new int[45]; 139 | int offset = 0; 140 | offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); 141 | return result; 142 | } 143 | 144 | private static int zzUnpackRowMap(String packed, int offset, int [] result) { 145 | int i = 0; /* index in packed string */ 146 | int j = offset; /* index in unpacked array */ 147 | int l = packed.length(); 148 | while (i < l) { 149 | int high = packed.charAt(i++) << 16; 150 | result[j++] = high | packed.charAt(i++); 151 | } 152 | return j; 153 | } 154 | 155 | /** 156 | * The transition table of the DFA 157 | */ 158 | private static final int [] ZZ_TRANS = zzUnpackTrans(); 159 | 160 | private static final String ZZ_TRANS_PACKED_0 = 161 | "\1\3\1\4\1\5\1\3\1\6\1\7\2\3\1\10"+ 162 | "\1\11\2\3\1\12\1\3\1\13\3\3\1\14\1\3"+ 163 | "\1\15\2\3\1\16\1\3\1\17\1\20\1\0\2\21"+ 164 | "\1\22\12\21\1\23\16\21\35\0\1\4\42\0\1\10"+ 165 | "\31\0\1\24\1\0\1\10\2\0\1\25\5\0\1\25"+ 166 | "\31\0\1\26\44\0\1\27\30\0\1\30\6\0\2\21"+ 167 | "\1\0\12\21\1\0\16\21\2\0\1\31\4\0\1\32"+ 168 | "\5\0\1\33\2\0\1\34\1\0\1\35\1\0\1\36"+ 169 | "\1\37\1\0\1\40\1\41\13\0\1\42\26\0\1\43"+ 170 | "\1\0\1\43\2\0\1\44\46\0\1\45\33\0\1\46"+ 171 | "\40\0\1\47\13\0\1\50\1\0\2\50\3\0\4\50"+ 172 | "\21\0\1\42\2\0\1\25\5\0\1\25\22\0\1\44"+ 173 | "\51\0\1\47\30\0\1\51\31\0\1\52\22\0\1\53"+ 174 | "\1\0\2\53\3\0\4\53\21\0\1\54\1\0\2\54"+ 175 | "\3\0\4\54\21\0\1\55\1\0\2\55\3\0\4\55"+ 176 | "\11\0"; 177 | 178 | private static int [] zzUnpackTrans() { 179 | int [] result = new int[700]; 180 | int offset = 0; 181 | offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); 182 | return result; 183 | } 184 | 185 | private static int zzUnpackTrans(String packed, int offset, int [] result) { 186 | int i = 0; /* index in packed string */ 187 | int j = offset; /* index in unpacked array */ 188 | int l = packed.length(); 189 | while (i < l) { 190 | int count = packed.charAt(i++); 191 | int value = packed.charAt(i++); 192 | value--; 193 | do result[j++] = value; while (--count > 0); 194 | } 195 | return j; 196 | } 197 | 198 | 199 | /** Error code for "Unknown internal scanner error". */ 200 | private static final int ZZ_UNKNOWN_ERROR = 0; 201 | /** Error code for "could not match input". */ 202 | private static final int ZZ_NO_MATCH = 1; 203 | /** Error code for "pushback value was too large". */ 204 | private static final int ZZ_PUSHBACK_2BIG = 2; 205 | 206 | /** 207 | * Error messages for {@link #ZZ_UNKNOWN_ERROR}, {@link #ZZ_NO_MATCH}, and 208 | * {@link #ZZ_PUSHBACK_2BIG} respectively. 209 | */ 210 | private static final String ZZ_ERROR_MSG[] = { 211 | "Unknown internal scanner error", 212 | "Error: could not match input", 213 | "Error: pushback value was too large" 214 | }; 215 | 216 | /** 217 | * ZZ_ATTRIBUTE[aState] contains the attributes of state {@code aState} 218 | */ 219 | private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); 220 | 221 | private static final String ZZ_ATTRIBUTE_PACKED_0 = 222 | "\2\0\1\11\1\1\2\11\2\1\3\11\3\1\2\11"+ 223 | "\1\1\1\11\1\1\5\0\10\11\1\0\1\1\1\0"+ 224 | "\1\1\4\0\2\11\2\0\1\11"; 225 | 226 | private static int [] zzUnpackAttribute() { 227 | int [] result = new int[45]; 228 | int offset = 0; 229 | offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); 230 | return result; 231 | } 232 | 233 | private static int zzUnpackAttribute(String packed, int offset, int [] result) { 234 | int i = 0; /* index in packed string */ 235 | int j = offset; /* index in unpacked array */ 236 | int l = packed.length(); 237 | while (i < l) { 238 | int count = packed.charAt(i++); 239 | int value = packed.charAt(i++); 240 | do result[j++] = value; while (--count > 0); 241 | } 242 | return j; 243 | } 244 | 245 | /** Input device. */ 246 | private java.io.Reader zzReader; 247 | 248 | /** Current state of the DFA. */ 249 | private int zzState; 250 | 251 | /** Current lexical state. */ 252 | private int zzLexicalState = YYINITIAL; 253 | 254 | /** 255 | * This buffer contains the current text to be matched and is the source of the {@link #yytext()} 256 | * string. 257 | */ 258 | private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; 259 | 260 | /** Text position at the last accepting state. */ 261 | private int zzMarkedPos; 262 | 263 | /** Current text position in the buffer. */ 264 | private int zzCurrentPos; 265 | 266 | /** Marks the beginning of the {@link #yytext()} string in the buffer. */ 267 | private int zzStartRead; 268 | 269 | /** Marks the last character in the buffer, that has been read from input. */ 270 | private int zzEndRead; 271 | 272 | /** 273 | * Whether the scanner is at the end of file. 274 | * @see #yyatEOF 275 | */ 276 | private boolean zzAtEOF; 277 | 278 | /** 279 | * The number of occupied positions in {@link #zzBuffer} beyond {@link #zzEndRead}. 280 | * 281 | *

When a lead/high surrogate has been read from the input stream into the final 282 | * {@link #zzBuffer} position, this will have a value of 1; otherwise, it will have a value of 0. 283 | */ 284 | private int zzFinalHighSurrogate = 0; 285 | 286 | /** Number of newlines encountered up to the start of the matched text. */ 287 | @SuppressWarnings("unused") 288 | private int yyline; 289 | 290 | /** Number of characters from the last newline up to the start of the matched text. */ 291 | @SuppressWarnings("unused") 292 | private int yycolumn; 293 | 294 | /** Number of characters up to the start of the matched text. */ 295 | private long yychar; 296 | 297 | /** Whether the scanner is currently at the beginning of a line. */ 298 | @SuppressWarnings("unused") 299 | private boolean zzAtBOL = true; 300 | 301 | /** Whether the user-EOF-code has already been executed. */ 302 | @SuppressWarnings("unused") 303 | private boolean zzEOFDone; 304 | 305 | /* user code: */ 306 | private StringBuilder sb=new StringBuilder(); 307 | 308 | long getPosition(){ 309 | return yychar; 310 | } 311 | 312 | 313 | 314 | /** 315 | * Creates a new scanner 316 | * 317 | * @param in the java.io.Reader to read input from. 318 | */ 319 | Yylex(java.io.Reader in) { 320 | this.zzReader = in; 321 | } 322 | 323 | /** 324 | * Translates raw input code points to DFA table row 325 | */ 326 | private static int zzCMap(int input) { 327 | int offset = input & 255; 328 | return offset == input ? ZZ_CMAP_BLOCKS[offset] : ZZ_CMAP_BLOCKS[ZZ_CMAP_TOP[input >> 8] | offset]; 329 | } 330 | 331 | /** 332 | * Refills the input buffer. 333 | * 334 | * @return {@code false} iff there was new input. 335 | * @exception java.io.IOException if any I/O-Error occurs 336 | */ 337 | private boolean zzRefill() throws java.io.IOException { 338 | 339 | /* first: make room (if you can) */ 340 | if (zzStartRead > 0) { 341 | zzEndRead += zzFinalHighSurrogate; 342 | zzFinalHighSurrogate = 0; 343 | System.arraycopy(zzBuffer, zzStartRead, 344 | zzBuffer, 0, 345 | zzEndRead - zzStartRead); 346 | 347 | /* translate stored positions */ 348 | zzEndRead -= zzStartRead; 349 | zzCurrentPos -= zzStartRead; 350 | zzMarkedPos -= zzStartRead; 351 | zzStartRead = 0; 352 | } 353 | 354 | /* is the buffer big enough? */ 355 | if (zzCurrentPos >= zzBuffer.length - zzFinalHighSurrogate) { 356 | /* if not: blow it up */ 357 | char newBuffer[] = new char[zzBuffer.length * 2]; 358 | System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); 359 | zzBuffer = newBuffer; 360 | zzEndRead += zzFinalHighSurrogate; 361 | zzFinalHighSurrogate = 0; 362 | } 363 | 364 | /* fill the buffer with new input */ 365 | int requested = zzBuffer.length - zzEndRead; 366 | int numRead = zzReader.read(zzBuffer, zzEndRead, requested); 367 | 368 | /* not supposed to occur according to specification of java.io.Reader */ 369 | if (numRead == 0) { 370 | throw new java.io.IOException( 371 | "Reader returned 0 characters. See JFlex examples/zero-reader for a workaround."); 372 | } 373 | if (numRead > 0) { 374 | zzEndRead += numRead; 375 | if (Character.isHighSurrogate(zzBuffer[zzEndRead - 1])) { 376 | if (numRead == requested) { // We requested too few chars to encode a full Unicode character 377 | --zzEndRead; 378 | zzFinalHighSurrogate = 1; 379 | } else { // There is room in the buffer for at least one more char 380 | int c = zzReader.read(); // Expecting to read a paired low surrogate char 381 | if (c == -1) { 382 | return true; 383 | } else { 384 | zzBuffer[zzEndRead++] = (char)c; 385 | } 386 | } 387 | } 388 | /* potentially more input available */ 389 | return false; 390 | } 391 | 392 | /* numRead < 0 ==> end of stream */ 393 | return true; 394 | } 395 | 396 | 397 | /** 398 | * Closes the input reader. 399 | * 400 | * @throws java.io.IOException if the reader could not be closed. 401 | */ 402 | public final void yyclose() throws java.io.IOException { 403 | zzAtEOF = true; // indicate end of file 404 | zzEndRead = zzStartRead; // invalidate buffer 405 | 406 | if (zzReader != null) { 407 | zzReader.close(); 408 | } 409 | } 410 | 411 | 412 | /** 413 | * Resets the scanner to read from a new input stream. 414 | * 415 | *

Does not close the old reader. 416 | * 417 | *

All internal variables are reset, the old input stream cannot be reused (internal 418 | * buffer is discarded and lost). Lexical state is set to {@code ZZ_INITIAL}. 419 | * 420 | *

Internal scan buffer is resized down to its initial length, if it has grown. 421 | * 422 | * @param reader The new input stream. 423 | */ 424 | public final void yyreset(java.io.Reader reader) { 425 | zzReader = reader; 426 | zzEOFDone = false; 427 | yyResetPosition(); 428 | zzLexicalState = YYINITIAL; 429 | if (zzBuffer.length > ZZ_BUFFERSIZE) { 430 | zzBuffer = new char[ZZ_BUFFERSIZE]; 431 | } 432 | } 433 | 434 | /** 435 | * Resets the input position. 436 | */ 437 | private final void yyResetPosition() { 438 | zzAtBOL = true; 439 | zzAtEOF = false; 440 | zzCurrentPos = 0; 441 | zzMarkedPos = 0; 442 | zzStartRead = 0; 443 | zzEndRead = 0; 444 | zzFinalHighSurrogate = 0; 445 | yyline = 0; 446 | yycolumn = 0; 447 | yychar = 0L; 448 | } 449 | 450 | 451 | /** 452 | * Returns whether the scanner has reached the end of the reader it reads from. 453 | * 454 | * @return whether the scanner has reached EOF. 455 | */ 456 | public final boolean yyatEOF() { 457 | return zzAtEOF; 458 | } 459 | 460 | 461 | /** 462 | * Returns the current lexical state. 463 | * 464 | * @return the current lexical state. 465 | */ 466 | public final int yystate() { 467 | return zzLexicalState; 468 | } 469 | 470 | 471 | /** 472 | * Enters a new lexical state. 473 | * 474 | * @param newState the new lexical state 475 | */ 476 | public final void yybegin(int newState) { 477 | zzLexicalState = newState; 478 | } 479 | 480 | 481 | /** 482 | * Returns the text matched by the current regular expression. 483 | * 484 | * @return the matched text. 485 | */ 486 | public final String yytext() { 487 | return new String(zzBuffer, zzStartRead, zzMarkedPos-zzStartRead); 488 | } 489 | 490 | 491 | /** 492 | * Returns the character at the given position from the matched text. 493 | * 494 | *

It is equivalent to {@code yytext().charAt(pos)}, but faster. 495 | * 496 | * @param position the position of the character to fetch. A value from 0 to {@code yylength()-1}. 497 | * 498 | * @return the character at {@code position}. 499 | */ 500 | public final char yycharat(int position) { 501 | return zzBuffer[zzStartRead + position]; 502 | } 503 | 504 | 505 | /** 506 | * How many characters were matched. 507 | * 508 | * @return the length of the matched text region. 509 | */ 510 | public final int yylength() { 511 | return zzMarkedPos-zzStartRead; 512 | } 513 | 514 | 515 | /** 516 | * Reports an error that occurred while scanning. 517 | * 518 | *

In a well-formed scanner (no or only correct usage of {@code yypushback(int)} and a 519 | * match-all fallback rule) this method will only be called with things that 520 | * "Can't Possibly Happen". 521 | * 522 | *

If this method is called, something is seriously wrong (e.g. a JFlex bug producing a faulty 523 | * scanner etc.). 524 | * 525 | *

Usual syntax/scanner level error handling should be done in error fallback rules. 526 | * 527 | * @param errorCode the code of the error message to display. 528 | */ 529 | private static void zzScanError(int errorCode) { 530 | String message; 531 | try { 532 | message = ZZ_ERROR_MSG[errorCode]; 533 | } catch (ArrayIndexOutOfBoundsException e) { 534 | message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; 535 | } 536 | 537 | throw new Error(message); 538 | } 539 | 540 | 541 | /** 542 | * Pushes the specified amount of characters back into the input stream. 543 | * 544 | *

They will be read again by then next call of the scanning method. 545 | * 546 | * @param number the number of characters to be read again. This number must not be greater than 547 | * {@link #yylength()}. 548 | */ 549 | public void yypushback(int number) { 550 | if ( number > yylength() ) 551 | zzScanError(ZZ_PUSHBACK_2BIG); 552 | 553 | zzMarkedPos -= number; 554 | } 555 | 556 | 557 | 558 | 559 | /** 560 | * Resumes scanning until the next regular expression is matched, the end of input is encountered 561 | * or an I/O-Error occurs. 562 | * 563 | * @return the next token. 564 | * @exception java.io.IOException if any I/O-Error occurs. 565 | */ 566 | public Yytoken yylex() throws java.io.IOException, JsonException { 567 | int zzInput; 568 | int zzAction; 569 | 570 | // cached fields: 571 | int zzCurrentPosL; 572 | int zzMarkedPosL; 573 | int zzEndReadL = zzEndRead; 574 | char[] zzBufferL = zzBuffer; 575 | 576 | int [] zzTransL = ZZ_TRANS; 577 | int [] zzRowMapL = ZZ_ROWMAP; 578 | int [] zzAttrL = ZZ_ATTRIBUTE; 579 | 580 | while (true) { 581 | zzMarkedPosL = zzMarkedPos; 582 | 583 | yychar+= zzMarkedPosL-zzStartRead; 584 | 585 | zzAction = -1; 586 | 587 | zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; 588 | 589 | zzState = ZZ_LEXSTATE[zzLexicalState]; 590 | 591 | // set up zzAction for empty match case: 592 | int zzAttributes = zzAttrL[zzState]; 593 | if ( (zzAttributes & 1) == 1 ) { 594 | zzAction = zzState; 595 | } 596 | 597 | 598 | zzForAction: { 599 | while (true) { 600 | 601 | if (zzCurrentPosL < zzEndReadL) { 602 | zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); 603 | zzCurrentPosL += Character.charCount(zzInput); 604 | } 605 | else if (zzAtEOF) { 606 | zzInput = YYEOF; 607 | break zzForAction; 608 | } 609 | else { 610 | // store back cached positions 611 | zzCurrentPos = zzCurrentPosL; 612 | zzMarkedPos = zzMarkedPosL; 613 | boolean eof = zzRefill(); 614 | // get translated positions and possibly new buffer 615 | zzCurrentPosL = zzCurrentPos; 616 | zzMarkedPosL = zzMarkedPos; 617 | zzBufferL = zzBuffer; 618 | zzEndReadL = zzEndRead; 619 | if (eof) { 620 | zzInput = YYEOF; 621 | break zzForAction; 622 | } 623 | else { 624 | zzInput = Character.codePointAt(zzBufferL, zzCurrentPosL, zzEndReadL); 625 | zzCurrentPosL += Character.charCount(zzInput); 626 | } 627 | } 628 | int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMap(zzInput) ]; 629 | if (zzNext == -1) break zzForAction; 630 | zzState = zzNext; 631 | 632 | zzAttributes = zzAttrL[zzState]; 633 | if ( (zzAttributes & 1) == 1 ) { 634 | zzAction = zzState; 635 | zzMarkedPosL = zzCurrentPosL; 636 | if ( (zzAttributes & 8) == 8 ) break zzForAction; 637 | } 638 | 639 | } 640 | } 641 | 642 | // store back cached position 643 | zzMarkedPos = zzMarkedPosL; 644 | 645 | if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { 646 | zzAtEOF = true; 647 | return null; 648 | } 649 | else { 650 | switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { 651 | case 1: 652 | { throw new JsonException(yychar, JsonException.Problems.UNEXPECTED_CHARACTER, new Character(yycharat(0))); 653 | } 654 | // fall through 655 | case 24: break; 656 | case 2: 657 | { 658 | } 659 | // fall through 660 | case 25: break; 661 | case 3: 662 | { sb = null; sb = new StringBuilder(); yybegin(STRING_BEGIN); 663 | } 664 | // fall through 665 | case 26: break; 666 | case 4: 667 | { return new Yytoken(Yytoken.Types.COMMA, null); 668 | } 669 | // fall through 670 | case 27: break; 671 | case 5: 672 | { java.math.BigDecimal val= new java.math.BigDecimal(yytext()); return new Yytoken(Yytoken.Types.DATUM, val); 673 | } 674 | // fall through 675 | case 28: break; 676 | case 6: 677 | { return new Yytoken(Yytoken.Types.COLON, null); 678 | } 679 | // fall through 680 | case 29: break; 681 | case 7: 682 | { return new Yytoken(Yytoken.Types.LEFT_SQUARE, null); 683 | } 684 | // fall through 685 | case 30: break; 686 | case 8: 687 | { return new Yytoken(Yytoken.Types.RIGHT_SQUARE, null); 688 | } 689 | // fall through 690 | case 31: break; 691 | case 9: 692 | { return new Yytoken(Yytoken.Types.LEFT_BRACE, null); 693 | } 694 | // fall through 695 | case 32: break; 696 | case 10: 697 | { return new Yytoken(Yytoken.Types.RIGHT_BRACE, null); 698 | } 699 | // fall through 700 | case 33: break; 701 | case 11: 702 | { sb.append(yytext()); 703 | } 704 | // fall through 705 | case 34: break; 706 | case 12: 707 | { yybegin(YYINITIAL);return new Yytoken(Yytoken.Types.DATUM, sb.toString()); 708 | } 709 | // fall through 710 | case 35: break; 711 | case 13: 712 | { sb.append('\\'); 713 | } 714 | // fall through 715 | case 36: break; 716 | case 14: 717 | { sb.append('"'); 718 | } 719 | // fall through 720 | case 37: break; 721 | case 15: 722 | { sb.append('/'); 723 | } 724 | // fall through 725 | case 38: break; 726 | case 16: 727 | { sb.append('\b'); 728 | } 729 | // fall through 730 | case 39: break; 731 | case 17: 732 | { sb.append('\f'); 733 | } 734 | // fall through 735 | case 40: break; 736 | case 18: 737 | { sb.append('\n'); 738 | } 739 | // fall through 740 | case 41: break; 741 | case 19: 742 | { sb.append('\r'); 743 | } 744 | // fall through 745 | case 42: break; 746 | case 20: 747 | { sb.append('\t'); 748 | } 749 | // fall through 750 | case 43: break; 751 | case 21: 752 | { return new Yytoken(Yytoken.Types.DATUM, null); 753 | } 754 | // fall through 755 | case 44: break; 756 | case 22: 757 | { Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.Types.DATUM, val); 758 | } 759 | // fall through 760 | case 45: break; 761 | case 23: 762 | { try{ 763 | int ch=Integer.parseInt(yytext().substring(2),16); 764 | sb.append((char)ch); 765 | }catch(Exception e){ 766 | /* The lexer is broken if it can build a 4 byte character code and fail to append the character. */ 767 | throw new JsonException(yychar, JsonException.Problems.UNEXPECTED_EXCEPTION, e); 768 | } 769 | } 770 | // fall through 771 | case 46: break; 772 | default: 773 | zzScanError(ZZ_NO_MATCH); 774 | } 775 | } 776 | } 777 | } 778 | 779 | 780 | } 781 | -------------------------------------------------------------------------------- /src/main/java/com/github/cliftonlabs/json_simple/Yytoken.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Clifton Labs 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. */ 11 | package com.github.cliftonlabs.json_simple; 12 | 13 | /** Represents structural entities in JSON. 14 | * @since 2.0.0 */ 15 | class Yytoken{ 16 | /** Represents the different kinds of tokens. */ 17 | enum Types{ 18 | /** Tokens of this type will always have a value of ":" */ 19 | COLON, 20 | /** Tokens of this type will always have a value of "," */ 21 | COMMA, 22 | /** Tokens of this type will always have a value that is a boolean, null, number, or string. */ 23 | DATUM, 24 | /** Tokens of this type will always have a value of "" */ 25 | END, 26 | /** Tokens of this type will always have a value of "{" */ 27 | LEFT_BRACE, 28 | /** Tokens of this type will always have a value of "[" */ 29 | LEFT_SQUARE, 30 | /** Tokens of this type will always have a value of "}" */ 31 | RIGHT_BRACE, 32 | /** Tokens of this type will always have a value of "]" */ 33 | RIGHT_SQUARE; 34 | } 35 | 36 | @SuppressWarnings("javadoc") 37 | private final Types type; 38 | @SuppressWarnings("javadoc") 39 | private final Object value; 40 | 41 | /** @param type represents the kind of token the instantiated token will be. 42 | * @param value represents the value the token is associated with, will be ignored unless type is equal to 43 | * Types.DATUM. 44 | * @see Types */ 45 | Yytoken(final Types type, final Object value){ 46 | /* Sanity check. Make sure the value is ignored for the proper value unless it is a datum token. */ 47 | switch(type){ 48 | case COLON: 49 | this.value = ":"; 50 | break; 51 | case COMMA: 52 | this.value = ","; 53 | break; 54 | case END: 55 | this.value = ""; 56 | break; 57 | case LEFT_BRACE: 58 | this.value = "{"; 59 | break; 60 | case LEFT_SQUARE: 61 | this.value = "["; 62 | break; 63 | case RIGHT_BRACE: 64 | this.value = "}"; 65 | break; 66 | case RIGHT_SQUARE: 67 | this.value = "]"; 68 | break; 69 | default: 70 | this.value = value; 71 | break; 72 | } 73 | this.type = type; 74 | } 75 | 76 | /** @return which of the {@link Yytoken.Types} the token is. 77 | * @see Yytoken.Types */ 78 | Types getType(){ 79 | return this.type; 80 | } 81 | 82 | /** @return what the token is. 83 | * @see Types */ 84 | Object getValue(){ 85 | return this.value; 86 | } 87 | 88 | @Override 89 | public String toString(){ 90 | final StringBuilder sb = new StringBuilder(); 91 | sb.append(this.type.toString()).append("(").append(this.value).append(")"); 92 | return sb.toString(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/lex/jsonstrict.lex: -------------------------------------------------------------------------------- 1 | package com.github.cliftonlabs.json_simple; 2 | 3 | %% 4 | 5 | %{ 6 | private StringBuilder sb=new StringBuilder(); 7 | 8 | long getPosition(){ 9 | return yychar; 10 | } 11 | 12 | %} 13 | 14 | %pack 15 | %unicode 16 | %state STRING_BEGIN 17 | 18 | %yylexthrow JsonException 19 | %char 20 | 21 | HEX_D = [a-fA-F0-9] 22 | DOUBLE = [-]?[0-9]+((\.[0-9]+)?([eE][-+]?[0-9]+)?) 23 | WS = [ \t\r\n] 24 | UNESCAPED_CH = [^\"\\] 25 | FALLBACK_CH = . 26 | %% 27 | 28 | \" { yybegin(YYINITIAL);return new Yytoken(Yytoken.Types.DATUM, sb.toString());} 29 | {UNESCAPED_CH}+ { sb.append(yytext());} 30 | \\\" {sb.append('"');} 31 | \\\\ {sb.append('\\');} 32 | \\\/ {sb.append('/');} 33 | \\b {sb.append('\b');} 34 | \\f {sb.append('\f');} 35 | \\n {sb.append('\n');} 36 | \\r {sb.append('\r');} 37 | \\t {sb.append('\t');} 38 | \\u{HEX_D}{HEX_D}{HEX_D}{HEX_D} { try{ 39 | int ch=Integer.parseInt(yytext().substring(2),16); 40 | sb.append((char)ch); 41 | }catch(Exception e){ 42 | /* The lexer is broken if it can build a 4 byte character code and fail to append the character. */ 43 | throw new JsonException(yychar, JsonException.Problems.UNEXPECTED_EXCEPTION, e); 44 | } 45 | } 46 | \\ {sb.append('\\');} 47 | 48 | \" { sb = null; sb = new StringBuilder(); yybegin(STRING_BEGIN);} 49 | {DOUBLE} { java.math.BigDecimal val= new java.math.BigDecimal(yytext()); return new Yytoken(Yytoken.Types.DATUM, val);} 50 | "true"|"false" { Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.Types.DATUM, val);} 51 | "null" { return new Yytoken(Yytoken.Types.DATUM, null);} 52 | "{" { return new Yytoken(Yytoken.Types.LEFT_BRACE, null);} 53 | "}" { return new Yytoken(Yytoken.Types.RIGHT_BRACE, null);} 54 | "[" { return new Yytoken(Yytoken.Types.LEFT_SQUARE, null);} 55 | "]" { return new Yytoken(Yytoken.Types.RIGHT_SQUARE, null);} 56 | "," { return new Yytoken(Yytoken.Types.COMMA, null);} 57 | ":" { return new Yytoken(Yytoken.Types.COLON, null);} 58 | {WS}+ {} 59 | {FALLBACK_CH} { throw new JsonException(yychar, JsonException.Problems.UNEXPECTED_CHARACTER, new Character(yycharat(0)));} 60 | -------------------------------------------------------------------------------- /src/test/java/com/github/cliftonlabs/json_simple/JsonArrayTest.java: -------------------------------------------------------------------------------- 1 | /* See: README for this file's copyright, terms, and conditions. */ 2 | package com.github.cliftonlabs.json_simple; 3 | 4 | import java.math.BigDecimal; 5 | import java.util.HashSet; 6 | import java.util.LinkedHashMap; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | import org.junit.After; 13 | import org.junit.Assert; 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | 17 | /** Ensures that JsonArray hasn't regressed in functionality or breaks its API contract. */ 18 | public class JsonArrayTest{ 19 | /** Called before each Test Method. */ 20 | @Before 21 | public void setUp(){ 22 | /* All of the implemented tests use local variables in their own respective method. */ 23 | } 24 | 25 | /** Called after each Test method. */ 26 | @After 27 | public void tearDown(){ 28 | /* All of the implemented tests use local variables in their own respective method. */ 29 | } 30 | 31 | /** Ensures the chain methods insert elements as expected. */ 32 | @Test 33 | public void testAddChains(){ 34 | final JsonArray allTest = new JsonArray(); 35 | allTest.add(6); 36 | allTest.add("seven"); 37 | final JsonArray json = new JsonArray(); 38 | final JsonArray chained = new JsonArray().addChain("asdf").addChain(0, 1234).addAllChain(allTest).addAllChain(3, allTest); 39 | json.add("asdf"); 40 | json.add(0, 1234); 41 | json.addAll(allTest); 42 | json.addAll(3, allTest); 43 | Assert.assertEquals(json, chained); 44 | } 45 | 46 | /** Ensures a homogeneous JsonArray can be viewed as another collection of a specific type. */ 47 | @Test 48 | public void testAsCollection(){ 49 | JsonArray json; 50 | LinkedList parameterList; 51 | HashSet parameterSet; 52 | json = new JsonArray(); 53 | json.add(1); 54 | json.add(2); 55 | json.add(3); 56 | json.add(4); 57 | json.add(5); 58 | parameterList = new LinkedList<>(); 59 | parameterSet = new HashSet<>(); 60 | json.asCollection(parameterList); 61 | json.asCollection(parameterSet); 62 | Assert.assertTrue(parameterList.contains(1)); 63 | Assert.assertTrue(parameterList.contains(2)); 64 | Assert.assertTrue(parameterList.contains(3)); 65 | Assert.assertTrue(parameterList.contains(4)); 66 | Assert.assertTrue(parameterList.contains(5)); 67 | Assert.assertTrue(parameterSet.contains(1)); 68 | Assert.assertTrue(parameterSet.contains(2)); 69 | Assert.assertTrue(parameterSet.contains(3)); 70 | Assert.assertTrue(parameterSet.contains(4)); 71 | Assert.assertTrue(parameterSet.contains(5)); 72 | } 73 | 74 | /** Ensures another collection can be used to instantiate a JsonArray. */ 75 | @Test 76 | public void testConstructor(){ 77 | JsonArray json; 78 | LinkedList parameterList; 79 | HashSet parameterSet; 80 | parameterList = new LinkedList<>(); 81 | parameterList.add(5); 82 | parameterList.add(10); 83 | parameterList.add(15); 84 | json = new JsonArray(parameterList); 85 | Assert.assertTrue(json.contains(5)); 86 | Assert.assertTrue(json.contains(10)); 87 | Assert.assertTrue(json.contains(15)); 88 | parameterSet = new HashSet<>(); 89 | parameterSet.add(20); 90 | parameterSet.add(25); 91 | parameterSet.add(30); 92 | json = new JsonArray(parameterSet); 93 | Assert.assertTrue(json.contains(20)); 94 | Assert.assertTrue(json.contains(25)); 95 | Assert.assertTrue(json.contains(30)); 96 | } 97 | 98 | /** Ensures a BigDecimal can be gotten if there is a BigDecimal, Number, or String at the index. */ 99 | @Test 100 | public void testGetBigDecimal(){ 101 | final JsonArray json = new JsonArray(); 102 | json.add(new BigDecimal("0")); 103 | json.add(Double.valueOf(0)); 104 | json.add(Float.valueOf(0)); 105 | json.add(Long.valueOf(0)); 106 | json.add(Integer.valueOf(0)); 107 | json.add(Short.valueOf((short)0)); 108 | json.add(Byte.valueOf((byte)0)); 109 | json.add(new String("0")); 110 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(0)); 111 | Assert.assertEquals(new BigDecimal("0.0"), json.getBigDecimal(1)); 112 | Assert.assertEquals(new BigDecimal("0.0"), json.getBigDecimal(2)); 113 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(3)); 114 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(4)); 115 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(5)); 116 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(6)); 117 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(7)); 118 | } 119 | 120 | /** Ensures a Collection can be returned from an index. */ 121 | @Test 122 | public void testGetCollection(){ 123 | final JsonArray json = new JsonArray(); 124 | LinkedList list; 125 | HashSet set; 126 | JsonArray array; 127 | List output0; 128 | Set output1; 129 | JsonArray output2; 130 | list = new LinkedList<>(); 131 | list.add(5); 132 | list.add(10); 133 | list.add(15); 134 | set = new HashSet<>(); 135 | set.add(20); 136 | set.add(25); 137 | set.add(30); 138 | array = new JsonArray(); 139 | array.add(35); 140 | array.add(40); 141 | array.add(45); 142 | json.add(list); 143 | json.add(set); 144 | json.add(array); 145 | output0 = json.getCollection(0); 146 | Assert.assertTrue(output0.contains(5)); 147 | Assert.assertTrue(output0.contains(10)); 148 | Assert.assertTrue(output0.contains(15)); 149 | output1 = json.getCollection(1); 150 | Assert.assertTrue(output1.contains(20)); 151 | Assert.assertTrue(output1.contains(25)); 152 | Assert.assertTrue(output1.contains(30)); 153 | output2 = json.getCollection(2); 154 | Assert.assertTrue(output2.contains(35)); 155 | Assert.assertTrue(output2.contains(40)); 156 | Assert.assertTrue(output2.contains(45)); 157 | } 158 | 159 | /** Ensure a map can be returned from an index. */ 160 | @Test 161 | public void testGetMap(){ 162 | final JsonArray json = new JsonArray(); 163 | final LinkedHashMap map = new LinkedHashMap<>(); 164 | final JsonObject object = new JsonObject(); 165 | Map output0; 166 | JsonObject output1; 167 | map.put("key0", 0); 168 | map.put("key1", 1); 169 | map.put("key2", 2); 170 | object.put("key3", 3); 171 | object.put("key4", 4); 172 | object.put("key5", 5); 173 | json.add(map); 174 | json.add(object); 175 | output0 = json.> getMap(0); 176 | Assert.assertTrue(output0.containsKey("key0")); 177 | Assert.assertTrue(output0.containsKey("key1")); 178 | Assert.assertTrue(output0.containsKey("key2")); 179 | Assert.assertTrue(output0.containsValue(0)); 180 | Assert.assertTrue(output0.containsValue(1)); 181 | Assert.assertTrue(output0.containsValue(2)); 182 | output1 = json. getMap(1); 183 | Assert.assertTrue(output1.containsKey("key3")); 184 | Assert.assertTrue(output1.containsKey("key4")); 185 | Assert.assertTrue(output1.containsKey("key5")); 186 | Assert.assertTrue(output1.containsValue(3)); 187 | Assert.assertTrue(output1.containsValue(4)); 188 | Assert.assertTrue(output1.containsValue(5)); 189 | } 190 | 191 | /** Ensures basic JSON values can be gotten. */ 192 | @Test 193 | public void testOtherJsonGets(){ 194 | final JsonArray json = new JsonArray(); 195 | json.add("101"); 196 | json.add(true); 197 | json.add(101); 198 | json.add(new BigDecimal("101")); 199 | json.add(null); 200 | /* Booleans are gotten from strings and booleans. */ 201 | Assert.assertEquals(true, json.getBoolean(1)); 202 | Assert.assertEquals(false, json.getBoolean(0)); 203 | /* Numbers are gotten from strings. */ 204 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByte(0)); 205 | Assert.assertEquals(Short.valueOf((short)101), json.getShort(0)); 206 | Assert.assertEquals(Integer.valueOf(101), json.getInteger(0)); 207 | Assert.assertEquals(Long.valueOf(101), json.getLong(0)); 208 | Assert.assertEquals(Float.valueOf(101), json.getFloat(0)); 209 | Assert.assertEquals(Double.valueOf(101), json.getDouble(0)); 210 | /* Numbers are gotten from numbers. */ 211 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByte(2)); 212 | Assert.assertEquals(Short.valueOf((short)101), json.getShort(2)); 213 | Assert.assertEquals(Integer.valueOf(101), json.getInteger(2)); 214 | Assert.assertEquals(Long.valueOf(101), json.getLong(2)); 215 | Assert.assertEquals(Float.valueOf(101), json.getFloat(2)); 216 | Assert.assertEquals(Double.valueOf(101), json.getDouble(2)); 217 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByte(3)); 218 | Assert.assertEquals(Short.valueOf((short)101), json.getShort(3)); 219 | Assert.assertEquals(Integer.valueOf(101), json.getInteger(3)); 220 | Assert.assertEquals(Long.valueOf(101), json.getLong(3)); 221 | Assert.assertEquals(Float.valueOf(101), json.getFloat(3)); 222 | Assert.assertEquals(Double.valueOf(101), json.getDouble(3)); 223 | /* Strings are gotten from booleans, numbers, and strings. */ 224 | Assert.assertEquals("101", json.getString(0)); 225 | Assert.assertEquals("true", json.getString(1)); 226 | Assert.assertEquals("101", json.getString(2)); 227 | Assert.assertEquals("101", json.getString(3)); 228 | /* Gets return null if the value is null. */ 229 | Assert.assertEquals(null, json.getString(4)); 230 | Assert.assertEquals(null, json.getBigDecimal(4)); 231 | Assert.assertEquals(null, json.getBoolean(4)); 232 | Assert.assertEquals(null, json.getByte(4)); 233 | Assert.assertEquals(null, json.getShort(4)); 234 | Assert.assertEquals(null, json.getInteger(4)); 235 | Assert.assertEquals(null, json.getLong(4)); 236 | Assert.assertEquals(null, json.getFloat(4)); 237 | Assert.assertEquals(null, json.getDouble(4)); 238 | Assert.assertEquals(null, json.get(4)); 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/test/java/com/github/cliftonlabs/json_simple/JsonObjectTest.java: -------------------------------------------------------------------------------- 1 | /* See: README for this file's copyright, terms, and conditions. */ 2 | package com.github.cliftonlabs.json_simple; 3 | 4 | import java.math.BigDecimal; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.LinkedHashMap; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.NoSuchElementException; 12 | import java.util.Set; 13 | 14 | import org.junit.After; 15 | import org.junit.Assert; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | 19 | /** Ensures that JsonObject hasn't regressed in functionality or breaks its API contract. */ 20 | public class JsonObjectTest{ 21 | @SuppressWarnings("javadoc") 22 | private enum TestEnums{ 23 | A, 24 | B; 25 | } 26 | 27 | @SuppressWarnings("javadoc") 28 | private static enum TestKeys implements JsonKey{ 29 | DNE(null), 30 | DNE_BIG_DECIMAL(new BigDecimal("101")), 31 | DNE_COLLECTION(new JsonArray()), 32 | DNE_ENUM(JsonObjectTest.TestEnums.A), 33 | DNE_MAP(new JsonObject()), 34 | DNE2(null), 35 | key0(null), 36 | key1(null), 37 | key2(null), 38 | key3(null), 39 | key4(null), 40 | key5(null), 41 | key6(null), 42 | key7(null), 43 | key8(null); 44 | 45 | private final Object value; 46 | 47 | private TestKeys(final Object value){ 48 | this.value = value; 49 | } 50 | 51 | @Override 52 | public String getKey(){ 53 | return this.name(); 54 | } 55 | 56 | @Override 57 | public Object getValue(){ 58 | return this.value; 59 | } 60 | } 61 | 62 | /** Called before each Test Method. */ 63 | @Before 64 | public void setUp(){ 65 | /* All of the implemented tests use local variables in their own respective method. */ 66 | } 67 | 68 | /** Called after each Test method. */ 69 | @After 70 | public void tearDown(){ 71 | /* All of the implemented tests use local variables in their own respective method. */ 72 | } 73 | 74 | /** Ensures another Map can be used to instantiate a JsonObject. */ 75 | @Test 76 | public void testConstructor(){ 77 | JsonObject json; 78 | LinkedHashMap parameter; 79 | parameter = new LinkedHashMap<>(); 80 | parameter.put("key0", 5); 81 | parameter.put("key1", 10); 82 | parameter.put("key2", 15); 83 | json = new JsonObject(parameter); 84 | Assert.assertTrue(json.containsKey("key0")); 85 | Assert.assertTrue(json.containsKey("key1")); 86 | Assert.assertTrue(json.containsKey("key2")); 87 | Assert.assertTrue(json.containsValue(5)); 88 | Assert.assertTrue(json.containsValue(10)); 89 | Assert.assertTrue(json.containsValue(15)); 90 | } 91 | 92 | /** Ensures a BigDecimal can be gotten if there is a BigDecimal, Number, or String at the key. */ 93 | @Test 94 | public void testGetBigDecimal(){ 95 | final JsonObject json = new JsonObject(); 96 | json.put(TestKeys.key0, new BigDecimal("0")); 97 | json.put(TestKeys.key1, Double.valueOf(0)); 98 | json.put(TestKeys.key2, Float.valueOf(0)); 99 | json.put(TestKeys.key3, Long.valueOf(0)); 100 | json.put(TestKeys.key4, Integer.valueOf(0)); 101 | json.put(TestKeys.key5, Short.valueOf((short)0)); 102 | json.put(TestKeys.key6, Byte.valueOf((byte)0)); 103 | json.put(TestKeys.key7, new String("0")); 104 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(TestKeys.key0)); 105 | Assert.assertEquals(new BigDecimal("0.0"), json.getBigDecimal(TestKeys.key1)); 106 | Assert.assertEquals(new BigDecimal("0.0"), json.getBigDecimal(TestKeys.key2)); 107 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(TestKeys.key3)); 108 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(TestKeys.key4)); 109 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(TestKeys.key5)); 110 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(TestKeys.key6)); 111 | Assert.assertEquals(new BigDecimal("0"), json.getBigDecimal(TestKeys.key7)); 112 | Assert.assertEquals(new BigDecimal("101"), json.getBigDecimalOrDefault(TestKeys.DNE_BIG_DECIMAL)); 113 | } 114 | 115 | /** Ensures a Collection can be returned from a key. */ 116 | @Test 117 | public void testGetCollection(){ 118 | final JsonObject json = new JsonObject(); 119 | LinkedList list; 120 | HashSet set; 121 | JsonArray array; 122 | List output0; 123 | Set output1; 124 | JsonArray output2; 125 | list = new LinkedList<>(); 126 | list.add(5); 127 | list.add(10); 128 | list.add(15); 129 | set = new HashSet<>(); 130 | set.add(20); 131 | set.add(25); 132 | set.add(30); 133 | array = new JsonArray(); 134 | array.add(35); 135 | array.add(40); 136 | array.add(45); 137 | json.put(TestKeys.key0, list); 138 | json.put(TestKeys.key1, set); 139 | json.put(TestKeys.key2, array); 140 | output0 = json.getCollection(TestKeys.key0); 141 | Assert.assertTrue(output0.contains(5)); 142 | Assert.assertTrue(output0.contains(10)); 143 | Assert.assertTrue(output0.contains(15)); 144 | output1 = json.getCollection(TestKeys.key1); 145 | Assert.assertTrue(output1.contains(20)); 146 | Assert.assertTrue(output1.contains(25)); 147 | Assert.assertTrue(output1.contains(30)); 148 | output2 = json.getCollection(TestKeys.key2); 149 | Assert.assertTrue(output2.contains(35)); 150 | Assert.assertTrue(output2.contains(40)); 151 | Assert.assertTrue(output2.contains(45)); 152 | Assert.assertEquals(new JsonArray(), json.getCollectionOrDefault(TestKeys.DNE_COLLECTION)); 153 | } 154 | 155 | /** Ensure a map can be returned from a key. */ 156 | @Test 157 | public void testGetMap(){ 158 | final JsonObject json = new JsonObject(); 159 | final LinkedHashMap map = new LinkedHashMap<>(); 160 | final JsonObject object = new JsonObject(); 161 | Map output0; 162 | JsonObject output1; 163 | map.put(TestKeys.key0.getKey(), 0); 164 | map.put(TestKeys.key1.getKey(), 1); 165 | map.put(TestKeys.key2.getKey(), 2); 166 | object.put(TestKeys.key3, 3); 167 | object.put(TestKeys.key4, 4); 168 | object.put(TestKeys.key5, 5); 169 | json.put(TestKeys.key6, map); 170 | json.put(TestKeys.key7, object); 171 | output0 = json.> getMap(TestKeys.key6); 172 | Assert.assertTrue(output0.containsKey(TestKeys.key0.getKey())); 173 | Assert.assertTrue(output0.containsKey(TestKeys.key1.getKey())); 174 | Assert.assertTrue(output0.containsKey(TestKeys.key2.getKey())); 175 | Assert.assertTrue(output0.containsValue(0)); 176 | Assert.assertTrue(output0.containsValue(1)); 177 | Assert.assertTrue(output0.containsValue(2)); 178 | output1 = json. getMap(TestKeys.key7); 179 | Assert.assertTrue(output1.containsKey(TestKeys.key3.getKey())); 180 | Assert.assertTrue(output1.containsKey(TestKeys.key4.getKey())); 181 | Assert.assertTrue(output1.containsKey(TestKeys.key5.getKey())); 182 | Assert.assertTrue(output1.containsValue(3)); 183 | Assert.assertTrue(output1.containsValue(4)); 184 | Assert.assertTrue(output1.containsValue(5)); 185 | Assert.assertEquals(new JsonObject(), json.getMapOrDefault(TestKeys.DNE_MAP)); 186 | } 187 | 188 | /** Ensures basic JSON values can be gotten. */ 189 | @Test 190 | public void testOtherJsonGets(){ 191 | final JsonObject json = new JsonObject(); 192 | /* Key0 -> string 193 | * key1 -> boolean 194 | * key2 -> number 195 | * key3 -> big decimal 196 | * key4 -> null 197 | * TestKeys need to swap values once in a while. */ 198 | json.put(TestKeys.key0, "101"); 199 | json.put(TestKeys.key1, true); 200 | json.put(TestKeys.key2, 101); 201 | json.put(TestKeys.key3, new BigDecimal("101")); 202 | json.put(TestKeys.key4, null); 203 | /* Booleans are gotten from strings and booleans. */ 204 | Assert.assertEquals(true, json.getBoolean(TestKeys.key1)); 205 | Assert.assertEquals(false, json.getBoolean(TestKeys.key0)); 206 | Assert.assertEquals(true, json.getBooleanOrDefault(Jsoner.mintJsonKey("key1", false))); 207 | Assert.assertEquals(false, json.getBooleanOrDefault(Jsoner.mintJsonKey("key0", true))); 208 | /* Numbers are gotten from strings. */ 209 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByte(TestKeys.key0)); 210 | Assert.assertEquals(Short.valueOf((short)101), json.getShort(TestKeys.key0)); 211 | Assert.assertEquals(Integer.valueOf(101), json.getInteger(TestKeys.key0)); 212 | Assert.assertEquals(Long.valueOf(101), json.getLong(TestKeys.key0)); 213 | Assert.assertEquals(Float.valueOf(101), json.getFloat(TestKeys.key0)); 214 | Assert.assertEquals(Double.valueOf(101), json.getDouble(TestKeys.key0)); 215 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByteOrDefault(Jsoner.mintJsonKey("key0", (byte)0))); 216 | Assert.assertEquals(Short.valueOf((short)101), json.getShortOrDefault(Jsoner.mintJsonKey("key0", (short)0))); 217 | Assert.assertEquals(Integer.valueOf(101), json.getIntegerOrDefault(Jsoner.mintJsonKey("key0", 0))); 218 | Assert.assertEquals(Long.valueOf(101), json.getLongOrDefault(Jsoner.mintJsonKey("key0", 0))); 219 | Assert.assertEquals(Float.valueOf(101), json.getFloatOrDefault(Jsoner.mintJsonKey("key0", 0))); 220 | Assert.assertEquals(Double.valueOf(101), json.getDoubleOrDefault(Jsoner.mintJsonKey("key0", 0))); 221 | /* Numbers are gotten from numbers. */ 222 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByte(TestKeys.key2)); 223 | Assert.assertEquals(Short.valueOf((short)101), json.getShort(TestKeys.key2)); 224 | Assert.assertEquals(Integer.valueOf(101), json.getInteger(TestKeys.key2)); 225 | Assert.assertEquals(Long.valueOf(101), json.getLong(TestKeys.key2)); 226 | Assert.assertEquals(Float.valueOf(101), json.getFloat(TestKeys.key2)); 227 | Assert.assertEquals(Double.valueOf(101), json.getDouble(TestKeys.key2)); 228 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByteOrDefault(Jsoner.mintJsonKey("key2", (byte)0))); 229 | Assert.assertEquals(Short.valueOf((short)101), json.getShortOrDefault(Jsoner.mintJsonKey("key2", (short)0))); 230 | Assert.assertEquals(Integer.valueOf(101), json.getIntegerOrDefault(Jsoner.mintJsonKey("key2", 0))); 231 | Assert.assertEquals(Long.valueOf(101), json.getLongOrDefault(Jsoner.mintJsonKey("key2", 0))); 232 | Assert.assertEquals(Float.valueOf(101), json.getFloatOrDefault(Jsoner.mintJsonKey("key2", 0))); 233 | Assert.assertEquals(Double.valueOf(101), json.getDoubleOrDefault(Jsoner.mintJsonKey("key2", 0))); 234 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByte(TestKeys.key3)); 235 | Assert.assertEquals(Short.valueOf((short)101), json.getShort(TestKeys.key3)); 236 | Assert.assertEquals(Integer.valueOf(101), json.getInteger(TestKeys.key3)); 237 | Assert.assertEquals(Long.valueOf(101), json.getLong(TestKeys.key3)); 238 | Assert.assertEquals(Float.valueOf(101), json.getFloat(TestKeys.key3)); 239 | Assert.assertEquals(Double.valueOf(101), json.getDouble(TestKeys.key3)); 240 | Assert.assertEquals(Byte.valueOf((byte)101), json.getByteOrDefault(Jsoner.mintJsonKey("key3", (byte)0))); 241 | Assert.assertEquals(Short.valueOf((short)101), json.getShortOrDefault(Jsoner.mintJsonKey("key3", (short)0))); 242 | Assert.assertEquals(Integer.valueOf(101), json.getIntegerOrDefault(Jsoner.mintJsonKey("key3", 0))); 243 | Assert.assertEquals(Long.valueOf(101), json.getLongOrDefault(Jsoner.mintJsonKey("key3", 0))); 244 | Assert.assertEquals(Float.valueOf(101), json.getFloatOrDefault(Jsoner.mintJsonKey("key3", 0))); 245 | Assert.assertEquals(Double.valueOf(101), json.getDoubleOrDefault(Jsoner.mintJsonKey("key3", 0))); 246 | /* Strings are gotten from booleans, numbers, and strings. */ 247 | Assert.assertEquals("101", json.getString(TestKeys.key0)); 248 | Assert.assertEquals("true", json.getString(TestKeys.key1)); 249 | Assert.assertEquals("101", json.getString(TestKeys.key2)); 250 | Assert.assertEquals("101", json.getString(TestKeys.key3)); 251 | Assert.assertEquals("101", json.getStringOrDefault(Jsoner.mintJsonKey("key0", "failed"))); 252 | Assert.assertEquals("true", json.getStringOrDefault(Jsoner.mintJsonKey("key1", "failed"))); 253 | Assert.assertEquals("101", json.getStringOrDefault(Jsoner.mintJsonKey("key2", "failed"))); 254 | Assert.assertEquals("101", json.getStringOrDefault(Jsoner.mintJsonKey("key3", "failed"))); 255 | /* Gets return null if the value is null. */ 256 | Assert.assertEquals(null, json.getStringOrDefault(Jsoner.mintJsonKey("key4", ""))); 257 | Assert.assertEquals(null, json.getBigDecimalOrDefault(Jsoner.mintJsonKey("key4", new BigDecimal(0)))); 258 | Assert.assertEquals(null, json.getBooleanOrDefault(Jsoner.mintJsonKey("key4", true))); 259 | Assert.assertEquals(null, json.getByteOrDefault(Jsoner.mintJsonKey("key4", (byte)0))); 260 | Assert.assertEquals(null, json.getShortOrDefault(Jsoner.mintJsonKey("key4", (short)0))); 261 | Assert.assertEquals(null, json.getIntegerOrDefault(Jsoner.mintJsonKey("key4", 0))); 262 | Assert.assertEquals(null, json.getLongOrDefault(Jsoner.mintJsonKey("key4", 0L))); 263 | Assert.assertEquals(null, json.getFloatOrDefault(Jsoner.mintJsonKey("key4", 0))); 264 | Assert.assertEquals(null, json.getDoubleOrDefault(Jsoner.mintJsonKey("key4", 0))); 265 | Assert.assertEquals(null, json.getString(TestKeys.key4)); 266 | Assert.assertEquals(null, json.getBigDecimal(TestKeys.key4)); 267 | Assert.assertEquals(null, json.getBoolean(TestKeys.key4)); 268 | Assert.assertEquals(null, json.getByte(TestKeys.key4)); 269 | Assert.assertEquals(null, json.getShort(TestKeys.key4)); 270 | Assert.assertEquals(null, json.getInteger(TestKeys.key4)); 271 | Assert.assertEquals(null, json.getLong(TestKeys.key4)); 272 | Assert.assertEquals(null, json.getFloat(TestKeys.key4)); 273 | Assert.assertEquals(null, json.getDouble(TestKeys.key4)); 274 | Assert.assertEquals(null, json.get(TestKeys.key4)); 275 | } 276 | 277 | /** Ensures the chain methods put entries as expected. */ 278 | @Test 279 | public void testPutChains(){ 280 | final Map testAll = new HashMap<>(); 281 | testAll.put("field1", "value1"); 282 | testAll.put("field2", 2); 283 | testAll.put("field3", "three"); 284 | final JsonObject json = new JsonObject(); 285 | final JsonObject chained = new JsonObject().putChain("field4", "4four").putAllChain(testAll); 286 | json.put("field4", "4four"); 287 | json.putAllChain(testAll); 288 | Assert.assertEquals(json, chained); 289 | } 290 | 291 | /** Ensures that when keys are present it does not throw NoSuchElementException. */ 292 | @Test 293 | public void testRequires(){ 294 | final JsonObject json = new JsonObject(); 295 | json.put(TestKeys.key0, 0); 296 | json.put(TestKeys.key1, 0); 297 | json.put(TestKeys.key2, 0); 298 | json.requireKeys(TestKeys.key0, TestKeys.key1); 299 | } 300 | 301 | /** Ensures that when required keys are not present the NoSuchElementException is thrown. */ 302 | @Test(expected = NoSuchElementException.class) 303 | public void testRequiresThrows(){ 304 | final JsonObject json = new JsonObject(); 305 | json.requireKeys(TestKeys.DNE, TestKeys.DNE2); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/test/java/com/github/cliftonlabs/json_simple/JsonerTest.java: -------------------------------------------------------------------------------- 1 | /* See: README for this file's copyright, terms, and conditions. */ 2 | package com.github.cliftonlabs.json_simple; 3 | 4 | import java.io.IOException; 5 | import java.io.StringReader; 6 | import java.io.StringWriter; 7 | import java.math.BigDecimal; 8 | 9 | import org.junit.After; 10 | import org.junit.Assert; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | /** Ensures that deserialization and serialization hasn't regressed in functionality or breaks its API contract. */ 15 | public class JsonerTest{ 16 | /** Called before each Test Method. */ 17 | @Before 18 | public void setUp(){ 19 | /* All of the implemented tests use local variables in their own respective method. */ 20 | } 21 | 22 | /** Called after each Test method. */ 23 | @After 24 | public void tearDown(){ 25 | /* All of the implemented tests use local variables in their own respective method. */ 26 | } 27 | 28 | /** Ensures arrays are directly deserializable. 29 | * @throws JsonException if the test fails. */ 30 | @Test 31 | public void testArrayDeserialization() throws JsonException{ 32 | JsonArray defaultValue; 33 | Object deserialized; 34 | /* Trailing commas are common causes of wasting time debugging JSON. Allowing it in deserialization will 35 | * inevitably make it feel more simple and save the user time debugging pointless things. */ 36 | deserialized = Jsoner.deserialize("[,]"); 37 | Assert.assertEquals(new JsonArray(), deserialized); 38 | /* Serializing JsonArrays directly requires a defaultValue in case it doesn't deserialize a JsonArray. */ 39 | defaultValue = new JsonArray(); 40 | defaultValue.add("default"); 41 | deserialized = Jsoner.deserialize("[,]", defaultValue); 42 | Assert.assertEquals(new JsonArray(), deserialized); 43 | /* The call should return the defaultValue instead. */ 44 | deserialized = Jsoner.deserialize("[asdf,]", defaultValue); 45 | Assert.assertEquals(defaultValue, deserialized); 46 | } 47 | 48 | /** Ensures arrays are serializable. 49 | * @throws IOException if the test fails. */ 50 | @Test 51 | public void testArraySerialization() throws IOException{ 52 | StringWriter serialized; 53 | /* Extraneous commas are not allowed when serializing an array. */ 54 | serialized = new StringWriter(); 55 | Jsoner.serialize(new JsonArray(), serialized); 56 | Assert.assertEquals("[]", serialized.toString()); 57 | serialized = new StringWriter(); 58 | Jsoner.serializeStrictly(new JsonArray(), serialized); 59 | Assert.assertEquals("[]", serialized.toString()); 60 | serialized = new StringWriter(); 61 | Jsoner.serializeCarelessly(new JsonArray(), serialized); 62 | Assert.assertEquals("[]", serialized.toString()); 63 | serialized = new StringWriter(); 64 | Jsoner.serialize(new boolean[]{true, false, true}, serialized); 65 | Assert.assertEquals("[true,false,true]", serialized.toString()); 66 | serialized = new StringWriter(); 67 | Jsoner.serialize(new byte[]{0, 1, 2}, serialized); 68 | Assert.assertEquals("[0,1,2]", serialized.toString()); 69 | serialized = new StringWriter(); 70 | Jsoner.serialize(new short[]{0, 1, 2}, serialized); 71 | Assert.assertEquals("[0,1,2]", serialized.toString()); 72 | serialized = new StringWriter(); 73 | Jsoner.serialize(new int[]{0, 1, 2}, serialized); 74 | Assert.assertEquals("[0,1,2]", serialized.toString()); 75 | serialized = new StringWriter(); 76 | Jsoner.serialize(new long[]{0, 1, 2}, serialized); 77 | Assert.assertEquals("[0,1,2]", serialized.toString()); 78 | serialized = new StringWriter(); 79 | Jsoner.serialize(new float[]{0.0f, 1.0f, 2.0f}, serialized); 80 | Assert.assertEquals("[0.0,1.0,2.0]", serialized.toString()); 81 | serialized = new StringWriter(); 82 | Jsoner.serialize(new double[]{0.0, 1.0, 2.0}, serialized); 83 | Assert.assertEquals("[0.0,1.0,2.0]", serialized.toString()); 84 | serialized = new StringWriter(); 85 | Jsoner.serialize(new char[]{'a', 'b', 'c'}, serialized); 86 | Assert.assertEquals("[\"a\",\"b\",\"c\"]", serialized.toString()); 87 | serialized = new StringWriter(); 88 | Jsoner.serialize(new Object[]{"a", "b", "d"}, serialized); 89 | Assert.assertEquals("[\"a\",\"b\",\"d\"]", serialized.toString()); 90 | } 91 | 92 | /** Ensures booleans are directly deserializable. 93 | * @throws JsonException if the test fails. */ 94 | @Test 95 | public void testBooleanDeserialization() throws JsonException{ 96 | Object deserialized; 97 | deserialized = Jsoner.deserialize("true"); 98 | Assert.assertEquals(true, deserialized); 99 | deserialized = Jsoner.deserialize("false"); 100 | Assert.assertEquals(false, deserialized); 101 | } 102 | 103 | /** Ensures booleans are serializable. 104 | * @throws IOException if the test fails. */ 105 | @Test 106 | public void testBooleanSerialization() throws IOException{ 107 | StringWriter serialized; 108 | serialized = new StringWriter(); 109 | Jsoner.serialize(true, serialized); 110 | Assert.assertEquals("true", serialized.toString()); 111 | serialized = new StringWriter(); 112 | Jsoner.serializeStrictly(true, serialized); 113 | Assert.assertEquals("true", serialized.toString()); 114 | serialized = new StringWriter(); 115 | Jsoner.serializeCarelessly(true, serialized); 116 | Assert.assertEquals("true", serialized.toString()); 117 | serialized = new StringWriter(); 118 | Jsoner.serialize(false, serialized); 119 | Assert.assertEquals("false", serialized.toString()); 120 | serialized = new StringWriter(); 121 | Jsoner.serializeStrictly(false, serialized); 122 | Assert.assertEquals("false", serialized.toString()); 123 | serialized = new StringWriter(); 124 | Jsoner.serializeCarelessly(false, serialized); 125 | Assert.assertEquals("false", serialized.toString()); 126 | } 127 | 128 | /** Ensures multiple concatenated JSON values are directly deserializable. 129 | * @throws JsonException if the test fails. */ 130 | @Test 131 | public void testDeserializationMany() throws JsonException{ 132 | final StringBuilder deserializable = new StringBuilder(); 133 | JsonArray expected; 134 | Object deserialized; 135 | /* Build the input string and the expected output one by one. */ 136 | expected = new JsonArray(); 137 | deserializable.append(false); 138 | expected.add(false); 139 | deserializable.append("{}"); 140 | expected.add(new JsonObject()); 141 | deserializable.append("{}"); 142 | expected.add(new JsonObject()); 143 | deserializable.append("{}"); 144 | expected.add(new JsonObject()); 145 | deserializable.append((String)null); 146 | expected.add(null); 147 | deserializable.append((String)null); 148 | expected.add(null); 149 | deserializable.append((String)null); 150 | expected.add(null); 151 | deserializable.append(true); 152 | expected.add(true); 153 | deserializable.append(true); 154 | expected.add(true); 155 | deserializable.append("[]"); 156 | expected.add(new JsonArray()); 157 | deserializable.append("123"); 158 | expected.add(new BigDecimal("123")); 159 | deserializable.append("{}"); 160 | expected.add(new JsonObject()); 161 | deserializable.append("[]"); 162 | expected.add(new JsonArray()); 163 | deserializable.append("12.3"); 164 | expected.add(new BigDecimal("12.3")); 165 | deserializable.append("\"\""); 166 | expected.add(""); 167 | deserializable.append("\"\\\"\\\"\""); 168 | expected.add("\"\""); 169 | deserializable.append("\"String\""); 170 | expected.add("String"); 171 | deserializable.append("12.3e-10"); 172 | expected.add(new BigDecimal("12.3e-10")); 173 | deserializable.append("[]"); 174 | expected.add(new JsonArray()); 175 | deserializable.append("[]"); 176 | expected.add(new JsonArray()); 177 | deserializable.append("[]"); 178 | expected.add(new JsonArray()); 179 | deserialized = Jsoner.deserializeMany(new StringReader(deserializable.toString())); 180 | Assert.assertEquals(expected, deserialized); 181 | } 182 | 183 | /** Ensures that compiler errors won't manifest in changes to the JsonException class. */ 184 | @Test 185 | public void testJsonExceptionHandling(){ 186 | final long position; 187 | final JsonException.Problems problem; 188 | final Object unexpectedObject; 189 | try{ 190 | throw new JsonException(0, JsonException.Problems.UNEXPECTED_CHARACTER, new Character('a')); 191 | }catch(final JsonException caught){ 192 | position = caught.getPosition(); 193 | problem = caught.getProblemType(); 194 | unexpectedObject = caught.getUnexpectedObject(); 195 | } 196 | Assert.assertEquals(0, position); 197 | Assert.assertEquals(JsonException.Problems.UNEXPECTED_CHARACTER, problem); 198 | Assert.assertEquals(new Character('a'), unexpectedObject); 199 | } 200 | 201 | /** Ensures booleans, JsonArray, JsonObject, null, numbers, and Strings are deserializable while inside a JsonObject 202 | * or JsonArray. 203 | * @throws JsonException if the test fails. */ 204 | @Test 205 | public void testNestedDeserialization() throws JsonException{ 206 | JsonArray expectedArray; 207 | JsonObject expectedObject; 208 | Object deserialized; 209 | /* Set up the expected array. */ 210 | expectedArray = new JsonArray(); 211 | expectedArray.add(true); 212 | expectedArray.add(false); 213 | expectedArray.add(new JsonArray()); 214 | expectedArray.add(new JsonObject()); 215 | expectedArray.add(null); 216 | expectedArray.add(new BigDecimal("-0.0e-100")); 217 | expectedArray.add("String"); 218 | /* Set up the expected object. */ 219 | expectedObject = new JsonObject(); 220 | expectedObject.put("key0", true); 221 | expectedObject.put("key1", false); 222 | expectedObject.put("key2", new JsonArray()); 223 | expectedObject.put("key3", new JsonObject()); 224 | expectedObject.put("key4", null); 225 | expectedObject.put("key5", new BigDecimal("-0.0e-100")); 226 | expectedObject.put("key6", "String"); 227 | /* Check that the nested serializations worked, with extra commas for good measure. */ 228 | deserialized = Jsoner.deserialize("[true,false,[],,{},null,-0.0e-100,,\"String\",]"); 229 | Assert.assertEquals(expectedArray, deserialized); 230 | deserialized = Jsoner.deserialize("{\"key0\":true,\"key1\":false,\"key2\":[],,\"key3\":{},,\"key4\":null,\"key5\":-0.0e-100,\"key6\":\"String\",}"); 231 | Assert.assertEquals(expectedObject, deserialized); 232 | } 233 | 234 | /** Ensures booleans, JsonArray, JsonObject, null, numbers, and Strings are serializable while inside a JsonObject 235 | * or JsonArray. 236 | * @throws IOException if the test failed. */ 237 | @Test 238 | public void testNestedSerialization() throws IOException{ 239 | JsonArray inputArray; 240 | JsonObject inputObject; 241 | StringWriter output; 242 | String serialized; 243 | /* Set up the input array. */ 244 | inputArray = new JsonArray(); 245 | inputArray.add(true); 246 | inputArray.add(false); 247 | inputArray.add(new JsonArray()); 248 | inputArray.add(new JsonObject()); 249 | inputArray.add(null); 250 | inputArray.add(new BigDecimal("-0.0e-100")); 251 | inputArray.add("String"); 252 | /* Set up the input object. */ 253 | inputObject = new JsonObject(); 254 | inputObject.put("key0", true); 255 | inputObject.put("key1", false); 256 | inputObject.put("key2", new JsonArray()); 257 | inputObject.put("key3", new JsonObject()); 258 | inputObject.put("key4", null); 259 | inputObject.put("key5", new BigDecimal("-0.0e-100")); 260 | inputObject.put("key6", "String"); 261 | /* Check that the nested serializations worked and should never have extraneous commas. 262 | * First check the array's serialization [normal, strictly, carelessly] output. */ 263 | output = new StringWriter(); 264 | Jsoner.serialize(inputArray, output); 265 | serialized = output.toString(); 266 | Assert.assertEquals("[true,false,[],{},null,0E-101,\"String\"]", output.toString()); 267 | output = new StringWriter(); 268 | Jsoner.serializeStrictly(inputArray, output); 269 | serialized = output.toString(); 270 | Assert.assertEquals("[true,false,[],{},null,0E-101,\"String\"]", output.toString()); 271 | output = new StringWriter(); 272 | Jsoner.serializeCarelessly(inputArray, output); 273 | serialized = output.toString(); 274 | Assert.assertEquals("[true,false,[],{},null,0E-101,\"String\"]", output.toString()); 275 | /* Next check the object's serialization [normal, strictly, carelessly] output. */ 276 | output = new StringWriter(); 277 | Jsoner.serialize(inputObject, output); 278 | serialized = output.toString(); 279 | /* Ensure it started with a '{' and ended with a '}'. */ 280 | Assert.assertTrue(serialized.charAt(0) == '{'); 281 | Assert.assertTrue(serialized.charAt(serialized.length() - 1) == '}'); 282 | /* Ensure each key and value were present in the correct format. */ 283 | Assert.assertTrue(serialized.contains("\"key0\":true")); 284 | Assert.assertTrue(serialized.contains("\"key1\":false")); 285 | Assert.assertTrue(serialized.contains("\"key2\":[]")); 286 | Assert.assertTrue(serialized.contains("\"key3\":{}")); 287 | Assert.assertTrue(serialized.contains("\"key4\":null")); 288 | Assert.assertTrue(serialized.contains("\"key5\":0E-101")); 289 | Assert.assertTrue(serialized.contains("\"key6\":\"String\"")); 290 | /* Ensure there were the correct amount of entries separated by a comma. */ 291 | Assert.assertTrue(serialized.split(",").length == 7); 292 | output = new StringWriter(); 293 | Jsoner.serializeStrictly(inputObject, output); 294 | serialized = output.toString(); 295 | /* Ensure it started with a '{' and ended with a '}'. */ 296 | Assert.assertTrue(serialized.charAt(0) == '{'); 297 | Assert.assertTrue(serialized.charAt(serialized.length() - 1) == '}'); 298 | /* Ensure each key and value were present in the correct format. */ 299 | Assert.assertTrue(serialized.contains("\"key0\":true")); 300 | Assert.assertTrue(serialized.contains("\"key1\":false")); 301 | Assert.assertTrue(serialized.contains("\"key2\":[]")); 302 | Assert.assertTrue(serialized.contains("\"key3\":{}")); 303 | Assert.assertTrue(serialized.contains("\"key4\":null")); 304 | Assert.assertTrue(serialized.contains("\"key5\":0E-101")); 305 | Assert.assertTrue(serialized.contains("\"key6\":\"String\"")); 306 | /* Ensure there were the correct amount of entries separated by a comma. */ 307 | Assert.assertTrue(serialized.split(",").length == 7); 308 | output = new StringWriter(); 309 | Jsoner.serializeCarelessly(inputObject, output); 310 | serialized = output.toString(); 311 | /* Ensure it started with a '{' and ended with a '}'. */ 312 | Assert.assertTrue(serialized.charAt(0) == '{'); 313 | Assert.assertTrue(serialized.charAt(serialized.length() - 1) == '}'); 314 | /* Ensure each key and value were present in the correct format. */ 315 | Assert.assertTrue(serialized.contains("\"key0\":true")); 316 | Assert.assertTrue(serialized.contains("\"key1\":false")); 317 | Assert.assertTrue(serialized.contains("\"key2\":[]")); 318 | Assert.assertTrue(serialized.contains("\"key3\":{}")); 319 | Assert.assertTrue(serialized.contains("\"key4\":null")); 320 | Assert.assertTrue(serialized.contains("\"key5\":0E-101")); 321 | Assert.assertTrue(serialized.contains("\"key6\":\"String\"")); 322 | /* Ensure there were the correct amount of entries separated by a comma. */ 323 | Assert.assertTrue(serialized.split(",").length == 7); 324 | } 325 | 326 | /** Ensures null is directly deserializable. 327 | * @throws JsonException if the test fails. */ 328 | @Test 329 | public void testNullDeserialization() throws JsonException{ 330 | Object deserialized; 331 | deserialized = Jsoner.deserialize("null"); 332 | Assert.assertEquals(null, deserialized); 333 | } 334 | 335 | /** Ensures null is serializable. 336 | * @throws IOException if the test fails. */ 337 | @Test 338 | public void testNullSerialization() throws IOException{ 339 | StringWriter serialized; 340 | serialized = new StringWriter(); 341 | Jsoner.serialize(null, serialized); 342 | Assert.assertEquals("null", serialized.toString()); 343 | serialized = new StringWriter(); 344 | Jsoner.serializeStrictly(null, serialized); 345 | Assert.assertEquals("null", serialized.toString()); 346 | serialized = new StringWriter(); 347 | Jsoner.serializeCarelessly(null, serialized); 348 | Assert.assertEquals("null", serialized.toString()); 349 | } 350 | 351 | /** Ensures Numbers are directly deserializable. 352 | * @throws JsonException if the test fails. */ 353 | @Test 354 | public void testNumberDeserialization() throws JsonException{ 355 | Object deserialized; 356 | deserialized = Jsoner.deserialize("-1234567890987654321.01234567890987654321E-50"); 357 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321E-50"), deserialized); 358 | deserialized = Jsoner.deserialize("-1234567890987654321.01234567890987654321"); 359 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321"), deserialized); 360 | deserialized = Jsoner.deserialize("1234567890987654321.01234567890987654321"); 361 | Assert.assertEquals(new BigDecimal("1234567890987654321.01234567890987654321"), deserialized); 362 | deserialized = Jsoner.deserialize("123456789098765432101234567890987654321"); 363 | Assert.assertEquals(new BigDecimal("123456789098765432101234567890987654321"), deserialized); 364 | } 365 | 366 | /** Ensures Numbers are serializable. 367 | * @throws IOException if the test fails. */ 368 | @Test 369 | public void testNumberSerialization() throws IOException{ 370 | StringWriter serialized; 371 | serialized = new StringWriter(); 372 | Jsoner.serialize(new BigDecimal("-1234567890987654321.01234567890987654321E-50"), serialized); 373 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321E-50").toString(), serialized.toString()); 374 | serialized = new StringWriter(); 375 | Jsoner.serializeStrictly(new BigDecimal("-1234567890987654321.01234567890987654321E-50"), serialized); 376 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321E-50").toString(), serialized.toString()); 377 | serialized = new StringWriter(); 378 | Jsoner.serializeCarelessly(new BigDecimal("-1234567890987654321.01234567890987654321E-50"), serialized); 379 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321E-50").toString(), serialized.toString()); 380 | serialized = new StringWriter(); 381 | Jsoner.serialize(new BigDecimal("-1234567890987654321.01234567890987654321"), serialized); 382 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321").toString(), serialized.toString()); 383 | serialized = new StringWriter(); 384 | Jsoner.serializeStrictly(new BigDecimal("-1234567890987654321.01234567890987654321"), serialized); 385 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321").toString(), serialized.toString()); 386 | serialized = new StringWriter(); 387 | Jsoner.serializeCarelessly(new BigDecimal("-1234567890987654321.01234567890987654321"), serialized); 388 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321").toString(), serialized.toString()); 389 | serialized = new StringWriter(); 390 | Jsoner.serialize(new BigDecimal("1234567890987654321.01234567890987654321"), serialized); 391 | Assert.assertEquals(new BigDecimal("1234567890987654321.01234567890987654321").toString(), serialized.toString()); 392 | serialized = new StringWriter(); 393 | Jsoner.serializeStrictly(new BigDecimal("1234567890987654321.01234567890987654321"), serialized); 394 | Assert.assertEquals(new BigDecimal("1234567890987654321.01234567890987654321").toString(), serialized.toString()); 395 | serialized = new StringWriter(); 396 | Jsoner.serializeCarelessly(new BigDecimal("1234567890987654321.01234567890987654321"), serialized); 397 | Assert.assertEquals(new BigDecimal("1234567890987654321.01234567890987654321").toString(), serialized.toString()); 398 | serialized = new StringWriter(); 399 | Jsoner.serialize(new BigDecimal("123456789098765432101234567890987654321"), serialized); 400 | Assert.assertEquals(new BigDecimal("123456789098765432101234567890987654321").toString(), serialized.toString()); 401 | serialized = new StringWriter(); 402 | Jsoner.serializeStrictly(new BigDecimal("123456789098765432101234567890987654321"), serialized); 403 | Assert.assertEquals(new BigDecimal("123456789098765432101234567890987654321").toString(), serialized.toString()); 404 | serialized = new StringWriter(); 405 | Jsoner.serializeCarelessly(new BigDecimal("123456789098765432101234567890987654321"), serialized); 406 | Assert.assertEquals(new BigDecimal("123456789098765432101234567890987654321").toString(), serialized.toString()); 407 | } 408 | 409 | /** Ensures objects are directly deserializable. 410 | * @throws JsonException if the test fails. */ 411 | @Test 412 | public void testObjectDeserialization() throws JsonException{ 413 | JsonObject defaultValue; 414 | Object deserialized; 415 | JsonObject expected; 416 | expected = new JsonObject(); 417 | defaultValue = new JsonObject(); 418 | defaultValue.put("error", -1); 419 | /* Trailing commas are common causes of wasting time debugging JSON. Allowing it in deserialization will 420 | * inevitably make it feel more simple and save the user time debugging pointless things. */ 421 | deserialized = Jsoner.deserialize("{,}"); 422 | Assert.assertEquals(expected, deserialized); 423 | /* A missing colon can be frustrating to track down and a waste of time debugging JSON. Allowing it in 424 | * deserialization will inevitably make it feel more simple and save the user time debugging things that don't 425 | * actually impede the library. */ 426 | expected.put("key", "value"); 427 | deserialized = Jsoner.deserialize("{\"key\"\"value\"}", defaultValue); 428 | Assert.assertEquals(expected, deserialized); 429 | /* Same thing but with numbers. */ 430 | expected.remove("key"); 431 | expected.put("key", new BigDecimal("234.0")); 432 | deserialized = Jsoner.deserialize("{\"key\"234.0}", defaultValue); 433 | Assert.assertEquals(expected, deserialized); 434 | /* Same thing but with booleans. */ 435 | expected.remove("key"); 436 | expected.put("key", true); 437 | deserialized = Jsoner.deserialize("{\"key\"true}", defaultValue); 438 | Assert.assertEquals(expected, deserialized); 439 | /* Same thing but with objects. */ 440 | expected.remove("key"); 441 | expected.put("key", new JsonObject()); 442 | deserialized = Jsoner.deserialize("{\"key\"{}}", defaultValue); 443 | Assert.assertEquals(expected, deserialized); 444 | /* Same thing but with arrays. */ 445 | expected.remove("key"); 446 | expected.put("key", new JsonArray()); 447 | deserialized = Jsoner.deserialize("{\"key\"[]}", defaultValue); 448 | Assert.assertEquals(expected, deserialized); 449 | /* Deserializing JsonObjects directly requires a defaultValue in case it doesn't deserialize a JsonObject. */ 450 | deserialized = Jsoner.deserialize("{asdf,}", defaultValue); 451 | Assert.assertEquals(defaultValue, deserialized); 452 | } 453 | 454 | /** Ensures objects are serializable. 455 | * @throws IOException if the test fails. */ 456 | @Test 457 | public void testObjectSerialization() throws IOException{ 458 | StringWriter serialized; 459 | /* Extraneous commas are not allowed when serializing an object. */ 460 | serialized = new StringWriter(); 461 | Jsoner.serialize(new JsonObject(), serialized); 462 | Assert.assertEquals("{}", serialized.toString()); 463 | serialized = new StringWriter(); 464 | Jsoner.serializeStrictly(new JsonObject(), serialized); 465 | Assert.assertEquals("{}", serialized.toString()); 466 | serialized = new StringWriter(); 467 | Jsoner.serializeCarelessly(new JsonObject(), serialized); 468 | Assert.assertEquals("{}", serialized.toString()); 469 | } 470 | 471 | /** Ensures arrays and objects can be printed in an easier to read format. */ 472 | @Test 473 | public void testPrettyPrint(){ 474 | Assert.assertEquals("[\n\t0,\n\t1,\n\t2,\n\t{\n\t\t\"k0\": \"v0\",\n\t\t\"k1\": \"v1\"\n\t},\n\t[\n\t\t[\n\t\t\t\"\",\n\t\t\t\"\"\n\t\t]\n\t],\n\tnull,\n\ttrue,\n\tfalse\n]", Jsoner.prettyPrint("[0,1,2,{\"k0\":\"v0\",\"k1\":\"v1\"},[[\"\",\"\"]],null,true,false]")); 475 | } 476 | 477 | /** Ensures Strings are directly deserializable. 478 | * @throws JsonException if the test fails. */ 479 | @Test 480 | public void testStringDeserialization() throws JsonException{ 481 | Object deserialized; 482 | /* Uses typical US English and picks out characters in unicode that have a decimal representation that ends with 483 | * 050, like 1050, 3050, 4050, 5050, etc. */ 484 | deserialized = Jsoner.deserialize("\"ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]\\/`123456789-=~!@#$%^&*_+()\\r\\b\\n\\t\\f\\\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ\\u4e2d\""); 485 | Assert.assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\r\b\n\t\f\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ\u4e2d", deserialized); 486 | } 487 | 488 | /** Ensures Strings are serializable. 489 | * @throws IOException if the test fails. */ 490 | @Test 491 | public void testStringSerialization() throws IOException{ 492 | StringWriter serialized; 493 | /* Uses typical US English and picks out characters in unicode that have a decimal representation that ends with 494 | * 050, like 1050, 3050, 4050, 5050, etc. */ 495 | serialized = new StringWriter(); 496 | Jsoner.serialize("ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\r\b\n\t\f\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ\u4e2d", serialized); 497 | Assert.assertEquals("\"ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\\r\\b\\n\\t\\f\\\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ中\"", serialized.toString()); 498 | serialized = new StringWriter(); 499 | Jsoner.serializeStrictly("ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\r\b\n\t\f\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ\u4e2d", serialized); 500 | Assert.assertEquals("\"ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\\r\\b\\n\\t\\f\\\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ中\"", serialized.toString()); 501 | serialized = new StringWriter(); 502 | Jsoner.serializeCarelessly("ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\r\b\n\t\f\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ\u4e2d", serialized); 503 | Assert.assertEquals("\"ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\\r\\b\\n\\t\\f\\\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ中\"", serialized.toString()); 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /src/test/java/com/github/cliftonlabs/json_simple/YylexTest.java: -------------------------------------------------------------------------------- 1 | /* See: README for this file's copyright, terms, and conditions. */ 2 | package com.github.cliftonlabs.json_simple; 3 | 4 | import java.io.IOException; 5 | import java.io.StringReader; 6 | import java.math.BigDecimal; 7 | 8 | import org.junit.After; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | /** Ensures the lexer hasn't regressed in functionality or breaks its API contract. */ 14 | public class YylexTest{ 15 | /** Called before each Test Method. */ 16 | @Before 17 | public void setUp(){ 18 | /* All of the implemented tests use local variables in their own respective method. */ 19 | } 20 | 21 | /** Called after each Test method. */ 22 | @After 23 | public void tearDown(){ 24 | /* All of the implemented tests use local variables in their own respective method. */ 25 | } 26 | 27 | /** Ensure concatenated JSON values are lexable. 28 | * @throws IOException if the test failed. 29 | * @throws JsonException if the test failed. */ 30 | @Test 31 | public void testLexingConcatenatedJsonValues() throws IOException, JsonException{ 32 | StringReader lexable; 33 | Yylex lexer; 34 | Yytoken lexed; 35 | lexable = new StringReader("nullnullnullnull12.33.21truetruenullfalse\"\"{}[]"); 36 | lexer = new Yylex(lexable); 37 | lexed = lexer.yylex(); 38 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 39 | Assert.assertEquals(null, lexed.getValue()); 40 | lexed = lexer.yylex(); 41 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 42 | Assert.assertEquals(null, lexed.getValue()); 43 | lexed = lexer.yylex(); 44 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 45 | Assert.assertEquals(null, lexed.getValue()); 46 | lexed = lexer.yylex(); 47 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 48 | Assert.assertEquals(null, lexed.getValue()); 49 | try{ 50 | lexed = lexer.yylex(); 51 | }catch(final JsonException caught){ 52 | /* Concatenated numbers don't always work well. */ 53 | Assert.assertEquals(JsonException.Problems.UNEXPECTED_EXCEPTION, caught.getProblemType()); 54 | } 55 | try{ 56 | lexed = lexer.yylex(); 57 | }catch(final JsonException caught){ 58 | /* Concatenated numbers don't always work well. */ 59 | Assert.assertEquals(JsonException.Problems.UNEXPECTED_CHARACTER, caught.getProblemType()); 60 | Assert.assertEquals(21, caught.getPosition()); 61 | } 62 | /* Instead of the 12.3 and 3.21 concatenated together we ended up with 21! */ 63 | lexed = lexer.yylex(); 64 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 65 | Assert.assertEquals(new BigDecimal("21"), lexed.getValue()); 66 | lexed = lexer.yylex(); 67 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 68 | Assert.assertEquals(true, lexed.getValue()); 69 | lexed = lexer.yylex(); 70 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 71 | Assert.assertEquals(true, lexed.getValue()); 72 | lexed = lexer.yylex(); 73 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 74 | Assert.assertEquals(null, lexed.getValue()); 75 | lexed = lexer.yylex(); 76 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 77 | Assert.assertEquals(false, lexed.getValue()); 78 | lexed = lexer.yylex(); 79 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 80 | Assert.assertEquals("", lexed.getValue()); 81 | lexed = lexer.yylex(); 82 | Assert.assertEquals(Yytoken.Types.LEFT_BRACE, lexed.getType()); 83 | lexed = lexer.yylex(); 84 | Assert.assertEquals(Yytoken.Types.RIGHT_BRACE, lexed.getType()); 85 | lexed = lexer.yylex(); 86 | Assert.assertEquals(Yytoken.Types.LEFT_SQUARE, lexed.getType()); 87 | lexed = lexer.yylex(); 88 | Assert.assertEquals(Yytoken.Types.RIGHT_SQUARE, lexed.getType()); 89 | } 90 | 91 | /** Ensures a negative number is lexable. 92 | * @throws IOException if the test fails. 93 | * @throws JsonException if the test fails. */ 94 | @Test 95 | public void testLexingNegativeNumber() throws IOException, JsonException{ 96 | StringReader lexable; 97 | Yylex lexer; 98 | Yytoken lexed; 99 | lexable = new StringReader("-123456789098765432101234567890987654321"); 100 | lexer = new Yylex(lexable); 101 | lexed = lexer.yylex(); 102 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 103 | Assert.assertEquals(new BigDecimal("-123456789098765432101234567890987654321"), lexed.getValue()); 104 | } 105 | 106 | /** Ensures a number with a decimal place in it is lexable. 107 | * @throws IOException if the test fails. 108 | * @throws JsonException if the test fails. */ 109 | @Test 110 | public void testLexingNumberWithDecimal() throws IOException, JsonException{ 111 | StringReader lexable; 112 | Yylex lexer; 113 | Yytoken lexed; 114 | lexable = new StringReader("-1234567890987654321.01234567890987654321"); 115 | lexer = new Yylex(lexable); 116 | lexed = lexer.yylex(); 117 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 118 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321"), lexed.getValue()); 119 | } 120 | 121 | /** Ensures a number with an exponent is lexable. 122 | * @throws IOException if the test fails. 123 | * @throws JsonException if the test fails. */ 124 | @Test 125 | public void testLexingNumberWithExponent() throws IOException, JsonException{ 126 | StringReader lexable; 127 | Yylex lexer; 128 | Yytoken lexed; 129 | lexable = new StringReader("-1234567890987654321.01234567890987654321E-50"); 130 | lexer = new Yylex(lexable); 131 | lexed = lexer.yylex(); 132 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 133 | Assert.assertEquals(new BigDecimal("-1234567890987654321.01234567890987654321E-50"), lexed.getValue()); 134 | } 135 | 136 | /** Ensures a positive number is lexable. 137 | * @throws IOException if the test fails. 138 | * @throws JsonException if the test fails. */ 139 | @Test 140 | public void testLexingPositiveNumber() throws IOException, JsonException{ 141 | StringReader lexable; 142 | Yylex lexer; 143 | Yytoken lexed; 144 | lexable = new StringReader("123456789098765432101234567890987654321"); 145 | lexer = new Yylex(lexable); 146 | lexed = lexer.yylex(); 147 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 148 | Assert.assertEquals(new BigDecimal("123456789098765432101234567890987654321"), lexed.getValue()); 149 | } 150 | 151 | /** Ensures a String containing escaped characters and various unicode characters is lexable. 152 | * @throws IOException if the test fails. 153 | * @throws JsonException if the test fails. */ 154 | @Test 155 | public void testLexingStringContainingEscapedCharacters() throws IOException, JsonException{ 156 | StringReader lexable; 157 | Yylex lexer; 158 | Yytoken lexed; 159 | lexable = new StringReader("\"ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]\\/`123456789-=~!@#$%^&*_+()\\r\\b\\n\\t\\f\\\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ\""); 160 | lexer = new Yylex(lexable); 161 | lexed = lexer.yylex(); 162 | Assert.assertEquals(Yytoken.Types.DATUM, lexed.getType()); 163 | Assert.assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ<>:{}abcdefghijklmnopqrstuvwxyz,.;'[]/`123456789-=~!@#$%^&*_+()\r\b\n\t\f\\К௪ၐᎺអὲ⍚❂⼒ぐ㋺ꁐꁚꑂ", lexed.getValue()); 164 | } 165 | 166 | /** Ensures that unexpected characters are a problem between expected characters. 167 | * @throws IOException if the test fails. 168 | * @throws JsonException if the test fails. */ 169 | @Test 170 | public void testLexingUnexpectedCharacter() throws IOException, JsonException{ 171 | StringReader lexable; 172 | Yylex lexer; 173 | Yytoken lexed; 174 | lexable = new StringReader("{a : b}"); 175 | lexer = new Yylex(lexable); 176 | lexed = lexer.yylex(); 177 | Assert.assertEquals(Yytoken.Types.LEFT_BRACE, lexed.getType()); 178 | try{ 179 | lexed = lexer.yylex(); 180 | }catch(final JsonException caught){ 181 | Assert.assertEquals(JsonException.Problems.UNEXPECTED_CHARACTER, caught.getProblemType()); 182 | Assert.assertEquals(new Character('a'), caught.getUnexpectedObject()); 183 | Assert.assertEquals(1, caught.getPosition()); 184 | } 185 | /* The exception should have left the lexed token unchanged. */ 186 | Assert.assertEquals(Yytoken.Types.LEFT_BRACE, lexed.getType()); 187 | } 188 | 189 | /** Ensure white space is ignored while lexing outside of Strings. 190 | * @throws IOException if the test fails. 191 | * @throws JsonException if the test fails. */ 192 | @Test 193 | public void testLexingWhiteSpace() throws IOException, JsonException{ 194 | StringReader lexable; 195 | Yylex lexer; 196 | Yytoken lexed; 197 | lexable = new StringReader("[\t \n\r\n{ \t \t\n\r}"); 198 | lexer = new Yylex(lexable); 199 | lexed = lexer.yylex(); 200 | Assert.assertEquals(Yytoken.Types.LEFT_SQUARE, lexed.getType()); 201 | lexed = lexer.yylex(); 202 | Assert.assertEquals(Yytoken.Types.LEFT_BRACE, lexed.getType()); 203 | lexed = lexer.yylex(); 204 | Assert.assertEquals(Yytoken.Types.RIGHT_BRACE, lexed.getType()); 205 | } 206 | } 207 | --------------------------------------------------------------------------------