pax_global_header00006660000000000000000000000064136376740340014527gustar00rootroot0000000000000052 comment=f968008a140414cd9a2505a071b2121688c07ea5 openjson-1.0.12/000077500000000000000000000000001363767403400134435ustar00rootroot00000000000000openjson-1.0.12/.gitignore000066400000000000000000000004641363767403400154370ustar00rootroot00000000000000*.class # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* # Maven target # IDEA .idea *.iml release.properties pom.xml.releaseBackup # Eclipse .classpath .project .settings openjson-1.0.12/.travis.yml000066400000000000000000000000571363767403400155560ustar00rootroot00000000000000language: java jdk: - openjdk8 - openjdk11 openjson-1.0.12/LICENSE.txt000066400000000000000000000261361363767403400152760ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. openjson-1.0.12/NOTICE000066400000000000000000000002251363767403400143460ustar00rootroot00000000000000Android JSON library Copyright (C) 2010 The Android Open Source Project This product includes software developed by The Android Open Source Project openjson-1.0.12/README.md000066400000000000000000000030161363767403400147220ustar00rootroot00000000000000# Open JSON [![Build Status](https://travis-ci.org/openjson/openjson.svg?branch=master)](https://travis-ci.org/openjson/openjson) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) This code is extracted from the Android project to allow a clean-room implementation of the popular JSON API to be available under a free license as a small and independent dependency. The [original library](http://www.json.org/) is [licensed under a standard BSD license with an additional line that requires the use of the software only for "non-evil" purposes](http://www.json.org/license.html). Since this is ill-defined, many downstream consumers of this software find this license condition unacceptable. The moral is don't put jokes into legal documents. More background information is collected by the Debian team members at . Relicensing the original library is impossible. See for more information. ## Maven ```xml com.github.openjson openjson 1.0.11 ``` # Acknowledgements Thanks to the Android team for doing 99% of the work. Thanks also to Simon Lessard for lending his critical eye and excellent suggestions. Thanks to Tobias Soloschenko and Martin Grigorov with suggestions so open-json can help [Apache Wicket](https://wicket.apache.org/) avoid the problem. openjson-1.0.12/pom.xml000066400000000000000000000223131363767403400147610ustar00rootroot00000000000000 4.0.0 com.github.openjson openjson 1.0.12 jar Open JSON A clean-room Apache-licensed implementation of simple JSON processing https://github.com/openjson/openjson The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo scm:git:https://github.com/openjson/openjson.git scm:git:https://github.com/openjson/openjson.git HEAD https://github.com/openjson/openjson openjson openjson team UTF-8 junit junit 4.12 test org.apache.maven.plugins maven-compiler-plugin 3.8.1 1.6 1.6 org.apache.maven.plugins maven-source-plugin 3.1.0 attach-sources jar org.apache.maven.plugins maven-javadoc-plugin 3.1.0 1.6 attach-javadocs jar org.apache.maven.plugins maven-release-plugin 2.5.3 org.apache.felix maven-bundle-plugin 3.5.1 true com.github.openjson http://www.apache.org/licenses/LICENSE-2.0 https://github.com/openjson/openjson bundle-manifest process-classes manifest org.apache.maven.plugins maven-jar-plugin 3.1.2 ${project.build.outputDirectory}/META-INF/MANIFEST.MF com.github.openjson org.jacoco jacoco-maven-plugin 0.8.4 pre-unit-test prepare-agent jacocoArgs post-unit-test test report org.apache.maven.plugins maven-surefire-plugin 2.22.2 ${jacocoArgs} ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ release performRelease true org.apache.maven.plugins maven-source-plugin 3.1.0 attach-sources jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 3.1.0 1.6 attach-javadocs jar org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign openjson-1.0.12/src/000077500000000000000000000000001363767403400142325ustar00rootroot00000000000000openjson-1.0.12/src/main/000077500000000000000000000000001363767403400151565ustar00rootroot00000000000000openjson-1.0.12/src/main/java/000077500000000000000000000000001363767403400160775ustar00rootroot00000000000000openjson-1.0.12/src/main/java/com/000077500000000000000000000000001363767403400166555ustar00rootroot00000000000000openjson-1.0.12/src/main/java/com/github/000077500000000000000000000000001363767403400201375ustar00rootroot00000000000000openjson-1.0.12/src/main/java/com/github/openjson/000077500000000000000000000000001363767403400217725ustar00rootroot00000000000000openjson-1.0.12/src/main/java/com/github/openjson/JSON.java000066400000000000000000000074671363767403400234240ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; class JSON { /** * Returns the input if it is a JSON-permissible value; throws otherwise. */ static double checkDouble(double d) throws JSONException { if (Double.isInfinite(d) || Double.isNaN(d)) { throw new JSONException("Forbidden numeric value: " + d); } return d; } static Boolean toBoolean(Object value) { if (value instanceof Boolean) { return (Boolean) value; } else if (value instanceof String) { String stringValue = (String) value; if ("true".equalsIgnoreCase(stringValue)) { return true; } else if ("false".equalsIgnoreCase(stringValue)) { return false; } } return null; } static Double toDouble(Object value) { if (value instanceof Double) { return (Double) value; } else if (value instanceof Number) { return ((Number) value).doubleValue(); } else if (value instanceof String) { try { return Double.valueOf((String) value); } catch (NumberFormatException ignored) { } } return null; } static Integer toInteger(Object value) { if (value instanceof Integer) { return (Integer) value; } else if (value instanceof Number) { return ((Number) value).intValue(); } else if (value instanceof String) { try { return (int) Double.parseDouble((String) value); } catch (NumberFormatException ignored) { } } return null; } static Long toLong(Object value) { if (value instanceof Long) { return (Long) value; } else if (value instanceof Number) { return ((Number) value).longValue(); } else if (value instanceof String) { try { return (long) Double.parseDouble((String) value); } catch (NumberFormatException ignored) { } } return null; } static String toString(Object value) { if (value instanceof String) { return (String) value; } else if (value != null) { return String.valueOf(value); } return null; } public static JSONException typeMismatch(Object indexOrName, Object actual, String requiredType) throws JSONException { if (actual == null) { throw new JSONException("Value at " + indexOrName + " is null."); } else { throw new JSONException("Value " + actual + " at " + indexOrName + " of type " + actual.getClass().getName() + " cannot be converted to " + requiredType); } } public static JSONException typeMismatch(Object actual, String requiredType) throws JSONException { if (actual == null) { throw new JSONException("Value is null."); } else { throw new JSONException("Value " + actual + " of type " + actual.getClass().getName() + " cannot be converted to " + requiredType); } } } openjson-1.0.12/src/main/java/com/github/openjson/JSONArray.java000066400000000000000000000650601363767403400244140ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * A dense indexed sequence of values. Values may be any mix of * {@link JSONObject JSONObjects}, other {@link JSONArray JSONArrays}, Strings, * Booleans, Integers, Longs, Doubles, {@code null} or {@link JSONObject#NULL}. * Values may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite() * infinities}, or of any type not listed here. * * {@code JSONArray} has the same type coercion behavior and * optional/mandatory accessors as {@link JSONObject}. See that class' * documentation for details. * * Warning: this class represents null in two incompatible * ways: the standard Java {@code null} reference, and the sentinel value {@link * JSONObject#NULL}. In particular, {@code get} fails if the requested index * holds the null reference, but succeeds if it holds {@code JSONObject.NULL}. * * Instances of this class are not thread safe. Although this class is * non-final, it was not designed for inheritance and should not be subclassed. * In particular, self-use by overridable methods is not specified. See * Effective Java Item 17, "Design and Document or inheritance or else * prohibit it" for further information. */ public class JSONArray implements Iterable { private final List values; /** * Creates a {@code JSONArray} with no values. */ public JSONArray() { values = new ArrayList(); } /** * Creates a new {@code JSONArray} by copying all values from the given * collection. * * @param copyFrom a collection whose values are of supported types. * Unsupported values are not permitted and will yield an array in an * inconsistent state. */ /* Accept a raw type for API compatibility */ public JSONArray(Collection copyFrom) { this(); if (copyFrom != null) { for (Object aCopyFrom : copyFrom) { put(JSONObject.wrap(aCopyFrom)); } } } /** * Creates a new {@code JSONArray} with values from the next array in the * tokener. * * @param readFrom a tokener whose nextValue() method will yield a * {@code JSONArray}. * @throws JSONException if the parse fails or doesn't yield a * {@code JSONArray}. */ public JSONArray(JSONTokener readFrom) throws JSONException { /* * Getting the parser to populate this could get tricky. Instead, just * parse to temporary JSONArray and then steal the data from that. */ Object object = readFrom.nextValue(); if (object instanceof JSONArray) { values = ((JSONArray) object).values; } else { throw JSON.typeMismatch(object, "JSONArray"); } } /** * Creates a new {@code JSONArray} with values from the JSON string. * * @param json a JSON-encoded string containing an array. * @throws JSONException if the parse fails or doesn't yield a {@code * JSONArray}. */ public JSONArray(String json) throws JSONException { this(new JSONTokener(json)); } /** * Creates a new {@code JSONArray} with values from the given primitive array. * * @param array The values to use. * @throws JSONException if any of the values are non-finite double values (i.e. NaN or infinite) */ public JSONArray(Object array) throws JSONException { if (!array.getClass().isArray()) { throw new JSONException("Not a primitive array: " + array.getClass()); } final int length = Array.getLength(array); values = new ArrayList(length); for (int i = 0; i < length; ++i) { put(JSONObject.wrap(Array.get(array, i))); } } /** * @return Returns the number of values in this array. */ public int length() { return values.size(); } /** * Appends {@code value} to the end of this array. * * @param value The value to append. * @return this array. */ public JSONArray put(boolean value) { values.add(value); return this; } /** * Appends {@code value} to the end of this array. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this array. * @throws JSONException If the value is unacceptable. */ public JSONArray put(double value) throws JSONException { values.add(JSON.checkDouble(value)); return this; } /** * Appends {@code value} to the end of this array. * * @param value The value to append. * @return this array. */ public JSONArray put(int value) { values.add(value); return this; } /** * Appends {@code value} to the end of this array. * * @param value The value to append. * @return this array. */ public JSONArray put(long value) { values.add(value); return this; } /** * Appends {@code value} wrapped by {@link JSONArray} to the end of this array. * * @param value any collection. * @return this array. */ public JSONArray put(Collection value) { if (value == null) { return put((Object)null); } values.add(new JSONArray(value)); return this; } /** * Appends {@code value} to the end of this array. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * infinities}. Unsupported values are not permitted and will cause the * array to be in an inconsistent state. * @return this array. */ public JSONArray put(Object value) { values.add(value); return this; } /** * Same as {@link #put}, with added validity checks. * * @param value The value to append. */ void checkedPut(Object value) throws JSONException { if (value instanceof Number) { JSON.checkDouble(((Number) value).doubleValue()); } put(value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param index Where to put the value. * @param value The value to set. * @return this array. * @throws JSONException This should never happen. */ public JSONArray put(int index, boolean value) throws JSONException { return put(index, (Boolean) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param index Where to put the value. * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this array. * @throws JSONException If the value is not a finite value. */ public JSONArray put(int index, double value) throws JSONException { return put(index, (Double) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param index Where to put the value. * @param value The value to set. * @return this array. * @throws JSONException Should never actually happen. */ public JSONArray put(int index, int value) throws JSONException { return put(index, (Integer) value); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param index Where to put the value. * @param value The value to set. * @return this array. * @throws JSONException Should never actually happen. */ public JSONArray put(int index, long value) throws JSONException { return put(index, (Long) value); } /** * Sets the value at {@code index} to {@code value} wrapped into {@link JSONArray}, * null padding this array to the required length if necessary. If a value already * exists at {@code index}, it will be replaced. * * @param index Where to put the value. * @param value The value to set. * @return this array. * @throws JSONException Should never actually happen. */ public JSONArray put(int index, Collection value) throws JSONException { if (value == null) { return put(index, (Object)null); } return put(index, new JSONArray(value)); } /** * Sets the value at {@code index} to {@code value}, null padding this array * to the required length if necessary. If a value already exists at {@code * index}, it will be replaced. * * @param index Where to put the value. * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * infinities}. * @return this array. * @throws JSONException If the value cannot be represented as a finite double value. */ public JSONArray put(int index, Object value) throws JSONException { if (value instanceof Number) { // deviate from the original by checking all Numbers, not just floats & doubles JSON.checkDouble(((Number) value).doubleValue()); } while (values.size() <= index) { values.add(null); } values.set(index, value); return this; } /** * Returns true if this array has no value at {@code index}, or if its value * is the {@code null} reference or {@link JSONObject#NULL}. * * @param index Which value to check. * @return true if the value is null. */ public boolean isNull(int index) { Object value = opt(index); return value == null || value == JSONObject.NULL; } /** * Returns the value at {@code index}. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if this array has no value at {@code index}, or if * that value is the {@code null} reference. This method returns * normally if the value is {@code JSONObject#NULL}. */ public Object get(int index) throws JSONException { try { Object value = values.get(index); if (value == null) { throw new JSONException("Value at " + index + " is null."); } return value; } catch (IndexOutOfBoundsException e) { throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")"); } } /** * Returns the value at {@code index}, or null if the array has no value * at {@code index}. * * @param index Which value to get. * @return the value at the specified location. */ public Object opt(int index) { if (index < 0 || index >= values.size()) { return null; } return values.get(index); } /** * Removes and returns the value at {@code index}, or null if the array has no value * at {@code index}. * * @param index Which value to remove. * @return The value previously at the specified location. */ public Object remove(int index) { if (index < 0 || index >= values.size()) { return null; } return values.remove(index); } /** * Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a boolean. */ public boolean getBoolean(int index) throws JSONException { Object object = get(index); Boolean result = JSON.toBoolean(object); if (result == null) { throw JSON.typeMismatch(index, object, "boolean"); } return result; } /** * Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean. Returns false otherwise. * * @param index Which value to get. * @return the value at the specified location. */ public boolean optBoolean(int index) { return optBoolean(index, false); } /** * Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean. Returns {@code fallback} otherwise. * * @param index Which value to get. * @param fallback the fallback value to return if no value exists. * @return the value at the specified location or the fallback value. */ public boolean optBoolean(int index, boolean fallback) { Object object = opt(index); Boolean result = JSON.toBoolean(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is a double or can * be coerced to a double. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a double. */ public double getDouble(int index) throws JSONException { Object object = get(index); Double result = JSON.toDouble(object); if (result == null) { throw JSON.typeMismatch(index, object, "double"); } return result; } /** * Returns the value at {@code index} if it exists and is a double or can * be coerced to a double. Returns {@code NaN} otherwise. * * @param index Which value to get. * @return the value at the specified location. */ public double optDouble(int index) { return optDouble(index, Double.NaN); } /** * Returns the value at {@code index} if it exists and is a double or can * be coerced to a double. Returns {@code fallback} otherwise. * * @param index Which value to get. * @param fallback The fallback value to use if no value is at the specified location. * @return the value at the specified location or the fallback value. */ public double optDouble(int index, double fallback) { Object object = opt(index); Double result = JSON.toDouble(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is an int or * can be coerced to an int. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a int. */ public int getInt(int index) throws JSONException { Object object = get(index); Integer result = JSON.toInteger(object); if (result == null) { throw JSON.typeMismatch(index, object, "int"); } return result; } /** * Returns the value at {@code index} if it exists and is an int or * can be coerced to an int. Returns 0 otherwise. * * @param index Which value to get. * @return the value at the specified location. */ public int optInt(int index) { return optInt(index, 0); } /** * Returns the value at {@code index} if it exists and is an int or * can be coerced to an int. Returns {@code fallback} otherwise. * * @param index Which value to get. * @param fallback The fallback value to use if no value is at the specified location. * @return the value at the specified location or the fallback value. */ public int optInt(int index, int fallback) { Object object = opt(index); Integer result = JSON.toInteger(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is a long or * can be coerced to a long. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if the value at {@code index} doesn't exist or * cannot be coerced to a long. */ public long getLong(int index) throws JSONException { Object object = get(index); Long result = JSON.toLong(object); if (result == null) { throw JSON.typeMismatch(index, object, "long"); } return result; } /** * Returns the value at {@code index} if it exists and is a long or * can be coerced to a long. Returns 0 otherwise. * * @param index Which value to get. * @return the value at the specified location. */ public long optLong(int index) { return optLong(index, 0L); } /** * Returns the value at {@code index} if it exists and is a long or * can be coerced to a long. Returns {@code fallback} otherwise. * * @param index Which value to get. * @param fallback The fallback value to use if no value is at the specified location. * @return the value at the specified location or the fallback value. */ public long optLong(int index, long fallback) { Object object = opt(index); Long result = JSON.toLong(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists, coercing it if * necessary. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if no such value exists. */ public String getString(int index) throws JSONException { Object object = get(index); String result = JSON.toString(object); if (result == null) { throw JSON.typeMismatch(index, object, "String"); } return result; } /** * Returns the value at {@code index} if it exists, coercing it if * necessary. Returns the empty string if no such value exists. * * @param index Which value to get. * @return the value at the specified location. */ public String optString(int index) { return optString(index, ""); } /** * Returns the value at {@code index} if it exists, coercing it if * necessary. Returns {@code fallback} if no such value exists. * * @param index Which value to get. * @param fallback The fallback value to use if no value is at the specified location. * @return the value at the specified location or the fallback value. */ public String optString(int index, String fallback) { Object object = opt(index); String result = JSON.toString(object); return result != null ? result : fallback; } /** * Returns the value at {@code index} if it exists and is a {@code * JSONArray}. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if the value doesn't exist or is not a {@code * JSONArray}. */ public JSONArray getJSONArray(int index) throws JSONException { Object object = get(index); if (object instanceof JSONArray) { return (JSONArray) object; } else { throw JSON.typeMismatch(index, object, "JSONArray"); } } /** * Returns the value at {@code index} if it exists and is a {@code * JSONArray}. Returns null otherwise. * * @param index Which value to get. * @return the value at the specified location. */ public JSONArray optJSONArray(int index) { Object object = opt(index); return object instanceof JSONArray ? (JSONArray) object : null; } /** * Returns the value at {@code index} if it exists and is a {@code * JSONObject}. * * @param index Which value to get. * @return the value at the specified location. * @throws JSONException if the value doesn't exist or is not a {@code * JSONObject}. */ public JSONObject getJSONObject(int index) throws JSONException { Object object = get(index); if (object instanceof JSONObject) { return (JSONObject) object; } else { throw JSON.typeMismatch(index, object, "JSONObject"); } } /** * Returns the value at {@code index} if it exists and is a {@code * JSONObject}. Returns null otherwise. * * @param index Which value to get. * @return the value at the specified location. */ public JSONObject optJSONObject(int index) { Object object = opt(index); return object instanceof JSONObject ? (JSONObject) object : null; } /** * Returns a new object whose values are the values in this array, and whose * names are the values in {@code names}. Names and values are paired up by * index from 0 through to the shorter array's length. Names that are not * strings will be coerced to strings. This method returns null if either * array is empty. * * @param names The names to apply to the returned values. * @return the newly constructed object. * @throws JSONException Should not be possible. */ public JSONObject toJSONObject(JSONArray names) throws JSONException { JSONObject result = new JSONObject(); int length = Math.min(names.length(), values.size()); if (length == 0) { return null; } for (int i = 0; i < length; i++) { String name = JSON.toString(names.opt(i)); result.put(name, opt(i)); } return result; } /** * Returns a new string by alternating this array's values with {@code * separator}. This array's string values are quoted and have their special * characters escaped. For example, the array containing the strings '12" * pizza', 'taco' and 'soda' joined on '+' returns this: *
"12\" pizza"+"taco"+"soda"
* * @param separator The string used to separate the returned values. * @return the conjoined values. * @throws JSONException Only if there is a coding error. */ public String join(String separator) throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.open(JSONStringer.Scope.NULL, ""); for (int i = 0, size = values.size(); i < size; i++) { if (i > 0) { stringer.out.append(separator); } stringer.value(values.get(i)); } stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); return stringer.out.toString(); } /** * Encodes this array as a compact JSON string, such as: *
[94043,90210]
* * Note 1: this method will not output any fields with 'null' value. * Override {@link JSONStringer#entry} method to have nulls printed. * * Note 2: this method will suppress any internal exceptions. * Use {@link JSONArray#toString(JSONStringer)} method directly to handle exceptions manually. * * @return The string form of this array. */ @Override public String toString() { try { return toString(new JSONStringer()); } catch (JSONException e) { return null; } } /** * Encodes this array as a human readable JSON string for debugging, such * as: *
     * [
     *     94043,
     *     90210
     * ]
* * @param indentSpaces the number of spaces to indent for each level of * nesting. * @return The string form of this array. * @throws JSONException Only if there is a coding error. */ public String toString(int indentSpaces) throws JSONException { return toString(new JSONStringer(indentSpaces)); } /** * Encodes this array using {@link JSONStringer} provided * * @param stringer - {@link JSONStringer} to be used for serialization * @return The string representation of this. * @throws JSONException On internal errors. Shouldn't happen. */ public String toString(JSONStringer stringer) throws JSONException { encode(stringer); return stringer.toString(); } /** * Encodes this array using {@link JSONStringer} provided * * @param stringer - {@link JSONStringer} to be used for serialization * @throws JSONException On internal errors. Shouldn't happen. */ protected void encode(JSONStringer stringer) { stringer.array(); for (Object value : values) { stringer.value(value); } stringer.endArray(); } @Override public Iterator iterator() { return values.iterator(); } @Override public boolean equals(Object o) { return o instanceof JSONArray && ((JSONArray) o).values.equals(values); } @Override public int hashCode() { // diverge from the original, which doesn't implement hashCode return values.hashCode(); } } openjson-1.0.12/src/main/java/com/github/openjson/JSONException.java000066400000000000000000000033171363767403400252710ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * Thrown to indicate a problem with the JSON API. Such problems include: *
    *
  • Attempts to parse or construct malformed documents *
  • Use of null as a name *
  • Use of numeric types not available to JSON, such as {@link * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. *
  • Lookups using an out of range index or nonexistent name *
  • Type mismatches on lookups *
* *

Although this is a checked exception, it is rarely recoverable. Most * callers should simply wrap this exception in an unchecked exception and * rethrow: *

  public JSONArray toJSONObject() {
 *     try {
 *         JSONObject result = new JSONObject();
 *         ...
 *     } catch (JSONException e) {
 *         throw new RuntimeException(e);
 *     }
 * }
*/ public class JSONException extends RuntimeException { public JSONException(String s) { super(s); } public JSONException(Throwable cause) { super(cause); } } openjson-1.0.12/src/main/java/com/github/openjson/JSONObject.java000066400000000000000000001130071363767403400245370ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * A modifiable set of name/value mappings. Names are unique, non-null strings. * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}. * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link * Double#isInfinite() infinities}, or of any type not listed here. * *

This class can coerce values to another type when requested. *

* *

This class can look up both mandatory and optional values: *

    *
  • Use getType() to retrieve a mandatory value. This * fails with a {@code JSONException} if the requested name has no value * or if the value cannot be coerced to the requested type. *
  • Use optType() to retrieve an optional value. This * returns a system- or user-supplied default if the requested name has no * value or if the value cannot be coerced to the requested type. *
* *

Warning: this class represents null in two incompatible * ways: the standard Java {@code null} reference, and the sentinel value {@link * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the * named entry from the object but {@code put(name, JSONObject.NULL)} stores an * entry whose value is {@code JSONObject.NULL}. * *

Instances of this class are not thread safe. Although this class is * nonfinal, it was not designed for inheritance and should not be subclassed. * In particular, self-use by overrideable methods is not specified. See * Effective Java Item 17, "Design and Document or inheritance or else * prohibit it" for further information. */ public class JSONObject { private static final Double NEGATIVE_ZERO = -0d; /** * A sentinel value used to explicitly define a name with no value. Unlike * {@code null}, names with this value: *

    *
  • show up in the {@link #names} array *
  • show up in the {@link #keys} iterator *
  • return {@code true} for {@link #has(String)} *
  • do not throw on {@link #get(String)} *
  • are included in the encoded JSON string. *
* *

This value violates the general contract of {@link Object#equals} by * returning true when compared to {@code null}. Its {@link #toString} * method returns "null". */ public static final Object NULL = new Object() { @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(Object o) { return o == this || o == null; // API specifies this broken equals implementation } // at least make the broken equals(null) consistent with Objects.hashCode(null). @Override public int hashCode() { return 0; } @Override public String toString() { return "null"; } }; private final LinkedHashMap nameValuePairs; /** * Creates a {@code JSONObject} with no name/value mappings. */ public JSONObject() { nameValuePairs = new LinkedHashMap(); } /** * Creates a new {@code JSONObject} by copying all name/value mappings from * the given map. * * @param copyFrom a map whose keys are of type {@link String} and whose * values are of supported types. * @throws NullPointerException if any of the map's keys are null. */ /* (accept a raw type for API compatibility) */ public JSONObject(Map copyFrom) { this(); init(copyFrom); } /** * Creates a new {@code JSONObject} with name/value mappings from the next * object in the tokener. * * @param readFrom a tokener whose nextValue() method will yield a * {@code JSONObject}. * @throws JSONException if the parse fails or doesn't yield a * {@code JSONObject}. */ public JSONObject(JSONTokener readFrom) throws JSONException { this(); init(readFrom); } /** * Creates a new {@code JSONObject} with name/value mappings from the JSON * string. * * @param json a JSON-encoded string containing an object. * @throws JSONException if the parse fails or doesn't yield a {@code * JSONObject}. */ public JSONObject(String json) throws JSONException { this(new JSONTokener(json)); } /** * Creates a new {@code JSONObject} by copying mappings for the listed names * from the given object. Names that aren't present in {@code copyFrom} will * be skipped. * * @param copyFrom The source object. * @param names The names of the fields to copy. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { this(); for (String name : names) { Object value = copyFrom.opt(name); if (value != null) { nameValuePairs.put(name, value); } } } /** * Creates a json object from a bean * @param bean the bean to create the json object from * @throws JSONException If there is an exception while reading the bean */ public JSONObject(Object bean) throws JSONException { this(); if (bean instanceof JSONObject) { init(((JSONObject)bean).nameValuePairs); } else if (bean instanceof JSONArray) { //no-op will produce empty object } else { init(objectAsMap(bean)); } } private void init(JSONTokener readFrom) { /* * Getting the parser to populate this could get tricky. Instead, just * parse to temporary JSONObject and then steal the data from that. */ Object object = readFrom.nextValue(); if (object instanceof JSONObject) { this.nameValuePairs.putAll(((JSONObject) object).nameValuePairs); } else { throw JSON.typeMismatch(object, "JSONObject"); } } private void init(Map copyFrom) { if (copyFrom != null) { for (Map.Entry entry : ((Map) copyFrom).entrySet()) { /* * Deviate from the original by checking that keys are non-null and * of the proper type. (We still defer validating the values). */ String key = (String) entry.getKey(); if (key == null) { throw new NullPointerException("key == null"); } nameValuePairs.put(key, wrap(entry.getValue())); } } } /** * Creates a name-value map from a bean * * @param bean the bean to create the map from * @return name-value map representing the bean * @throws JSONException If there is an exception while reading the bean */ public static Map objectAsMap(Object bean) throws JSONException { Map props = new TreeMap(); try { PropertyDescriptor[] properties = Introspector.getBeanInfo(bean.getClass(), Object.class) .getPropertyDescriptors(); for (PropertyDescriptor prop : properties) { Object v = prop.getReadMethod().invoke(bean); props.put(prop.getDisplayName(), wrap(v)); } } catch (IllegalAccessException e) { throw new JSONException(e); } catch (IntrospectionException e) { throw new JSONException(e); } catch (InvocationTargetException e) { throw new JSONException(e); } return props; } public static String[] getNames(JSONObject x) { Set names = x.keySet(); String[] r = new String[names.size()]; int i = 0; for (String name : names) { r[i++] = name; } return r; } /** * Returns the number of name/value mappings in this object. * * @return the length of this. */ public int length() { return nameValuePairs.size(); } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @param name The name of the value to insert. * @param value The value to insert. * @return this object. * @throws JSONException Should not be possible. */ public JSONObject put(String name, boolean value) throws JSONException { nameValuePairs.put(checkName(name), value); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @param name The name for the new value. * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this object. * @throws JSONException if value is NaN or infinite. */ public JSONObject put(String name, double value) throws JSONException { nameValuePairs.put(checkName(name), JSON.checkDouble(value)); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @param name The name for the new value. * @param value The new value. * @return this object. * @throws JSONException Should not be possible. */ public JSONObject put(String name, int value) throws JSONException { nameValuePairs.put(checkName(name), value); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. * * @param name The name of the new value. * @param value The new value to insert. * @return this object. * @throws JSONException Should not be possible. */ public JSONObject put(String name, long value) throws JSONException { nameValuePairs.put(checkName(name), value); return this; } /** * Maps {@code name} to {@code value}, clobbering any existing name/value * mapping with the same name. If the value is {@code null}, any existing * mapping for {@code name} is removed. * * @param name The name of the new value. * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be * {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * infinities}. * If value is Map or Collection the value is wrapped using * corresponding JSONObject(map) or JSONArray(collection) object. * This behavior is considered unsafe and is added for compatibility * with original 'org.json' package only. * @return this object. * @throws JSONException if the value is an invalid double (infinite or NaN). */ public JSONObject put(String name, Object value) throws JSONException { if (value == null) { nameValuePairs.remove(name); return this; } Object valueToPut = value; if (value instanceof Number) { // deviate from the original by checking all Numbers, not just floats & doubles JSON.checkDouble(((Number) value).doubleValue()); } else if (value instanceof Collection) { valueToPut = new JSONArray((Collection) value); } else if (value instanceof Map) { valueToPut = new JSONObject((Map)value); } nameValuePairs.put(checkName(name), valueToPut); return this; } /** * Equivalent to {@code put(name, value)} when both parameters are non-null; * does nothing otherwise. * * @param name The name of the value to insert. * @param value The value to insert. * @return this object. * @throws JSONException if the value is an invalid double (infinite or NaN). */ public JSONObject putOpt(String name, Object value) throws JSONException { if (name == null || value == null) { return this; } return put(name, value); } /** * Appends {@code value} to the array already mapped to {@code name}. If * this object has no mapping for {@code name}, this inserts a new mapping. * If the mapping exists but its value is not an array, the existing * and new values are inserted in order into a new array which is itself * mapped to {@code name}. In aggregate, this allows values to be added to a * mapping one at a time. * * Note that {@code append(String, Object)} provides better semantics. * In particular, the mapping for {@code name} will always be a * {@link JSONArray}. Using {@code accumulate} will result in either a * {@link JSONArray} or a mapping whose type is the type of {@code value} * depending on the number of calls to it. * * @param name The name of the field to change. * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double, {@link #NULL} or null. May not be {@link * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. * @return this object after mutation. * @throws JSONException If the object being added is an invalid number. */ // TODO: Change {@code append) to {@link #append} when append is // unhidden. public JSONObject accumulate(String name, Object value) throws JSONException { Object current = nameValuePairs.get(checkName(name)); if (current == null) { return put(name, value); } if (current instanceof JSONArray) { JSONArray array = (JSONArray) current; array.checkedPut(value); } else { JSONArray array = new JSONArray(); array.checkedPut(current); array.checkedPut(value); nameValuePairs.put(name, array); } return this; } /** * Appends values to the array mapped to {@code name}. A new {@link JSONArray} * mapping for {@code name} will be inserted if no mapping exists. If the existing * mapping for {@code name} is not a {@link JSONArray}, a {@link JSONException} * will be thrown. * * @param name The name of the array to which the value should be appended. * @param value The value to append. * @return this object. * @throws JSONException if {@code name} is {@code null} or if the mapping for * {@code name} is non-null and is not a {@link JSONArray}. */ public JSONObject append(String name, Object value) throws JSONException { Object current = nameValuePairs.get(checkName(name)); final JSONArray array; if (current instanceof JSONArray) { array = (JSONArray) current; } else if (current == null) { JSONArray newArray = new JSONArray(); nameValuePairs.put(name, newArray); array = newArray; } else { throw new JSONException("Key " + name + " is not a JSONArray"); } array.checkedPut(value); return this; } String checkName(String name) throws JSONException { if (name == null) { throw new JSONException("Names must be non-null"); } return name; } /** * Removes the named mapping if it exists; does nothing otherwise. * * @param name The name of the mapping to remove. * @return the value previously mapped by {@code name}, or null if there was * no such mapping. */ public Object remove(String name) { return nameValuePairs.remove(name); } /** * Returns true if this object has no mapping for {@code name} or if it has * a mapping whose value is {@link #NULL}. * * @param name The name of the value to check on. * @return true if the field doesn't exist or is null. */ public boolean isNull(String name) { Object value = nameValuePairs.get(name); return value == null || value == NULL; } /** * Returns true if this object has a mapping for {@code name}. The mapping * may be {@link #NULL}. * * @param name The name of the value to check on. * @return true if this object has a field named {@code name} */ public boolean has(String name) { return nameValuePairs.containsKey(name); } /** * Returns the value mapped by {@code name}, or throws if no such mapping exists. * * @param name The name of the value to get. * @return The value. * @throws JSONException if no such mapping exists. */ public Object get(String name) throws JSONException { Object result = nameValuePairs.get(name); if (result == null) { throw new JSONException("No value for " + name); } return result; } /** * Returns the value mapped by {@code name}, or null if no such mapping * exists. * * @param name The name of the value to get. * @return The value. */ public Object opt(String name) { return nameValuePairs.get(name); } /** * Returns the value mapped by {@code name} if it exists and is a boolean or * can be coerced to a boolean, or throws otherwise. * * @param name The name of the field we want. * @return The selected value if it exists. * @throws JSONException if the mapping doesn't exist or cannot be coerced * to a boolean. */ public boolean getBoolean(String name) throws JSONException { Object object = get(name); Boolean result = JSON.toBoolean(object); if (result == null) { throw JSON.typeMismatch(name, object, "boolean"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is a boolean or * can be coerced to a boolean, or false otherwise. * * @param name The name of the field we want. * @return The selected value if it exists. */ public boolean optBoolean(String name) { return optBoolean(name, false); } /** * Returns the value mapped by {@code name} if it exists and is a boolean or * can be coerced to a boolean, or {@code fallback} otherwise. * * @param name The name of the field we want. * @param fallback The value to return if the field isn't there. * @return The selected value or the fallback. */ public boolean optBoolean(String name, boolean fallback) { Object object = opt(name); Boolean result = JSON.toBoolean(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is a double or * can be coerced to a double, or throws otherwise. * * @param name The name of the field we want. * @return The selected value if it exists. * @throws JSONException if the mapping doesn't exist or cannot be coerced * to a double. */ public double getDouble(String name) throws JSONException { Object object = get(name); Double result = JSON.toDouble(object); if (result == null) { throw JSON.typeMismatch(name, object, "double"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is a double or * can be coerced to a double, or {@code NaN} otherwise. * * @param name The name of the field we want. * @return The selected value if it exists. */ public double optDouble(String name) { return optDouble(name, Double.NaN); } /** * Returns the value mapped by {@code name} if it exists and is a double or * can be coerced to a double, or {@code fallback} otherwise. * * @param name The name of the field we want. * @param fallback The value to return if the field isn't there. * @return The selected value or the fallback. */ public double optDouble(String name, double fallback) { Object object = opt(name); Double result = JSON.toDouble(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is an int or * can be coerced to an int, or throws otherwise. * * @param name The name of the field we want. * @return The selected value if it exists. * @throws JSONException if the mapping doesn't exist or cannot be coerced * to an int. */ public int getInt(String name) throws JSONException { Object object = get(name); Integer result = JSON.toInteger(object); if (result == null) { throw JSON.typeMismatch(name, object, "int"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is an int or * can be coerced to an int, or 0 otherwise. * * @param name The name of the field we want. * @return The selected value if it exists. */ public int optInt(String name) { return optInt(name, 0); } /** * Returns the value mapped by {@code name} if it exists and is an int or * can be coerced to an int, or {@code fallback} otherwise. * * @param name The name of the field we want. * @param fallback The value to return if the field isn't there. * @return The selected value or the fallback. */ public int optInt(String name, int fallback) { Object object = opt(name); Integer result = JSON.toInteger(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to a long, or throws otherwise. * Note that JSON represents numbers as doubles, * * so this is lossy; use strings to transfer numbers * via JSON without loss. * * @param name The name of the field that we want. * @return The value of the field. * @throws JSONException if the mapping doesn't exist or cannot be coerced * to a long. */ public long getLong(String name) throws JSONException { Object object = get(name); Long result = JSON.toLong(object); if (result == null) { throw JSON.typeMismatch(name, object, "long"); } return result; } /** * Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to a long, or 0 otherwise. Note that JSON represents numbers as doubles, * so this is lossy; use strings to transfer numbers via JSON. * * @param name The name of the field we want. * @return The selected value. */ public long optLong(String name) { return optLong(name, 0L); } /** * Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to a long, or {@code fallback} otherwise. Note that JSON represents * numbers as doubles, so this is lossy; use strings to transfer * numbers via JSON. * * @param name The name of the field we want. * @param fallback The value to return if the field isn't there. * @return The selected value or the fallback. */ public long optLong(String name, long fallback) { Object object = opt(name); Long result = JSON.toLong(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary, or throws if no such mapping exists. * * @param name The name of the field we want. * @return The value of the field. * @throws JSONException if no such mapping exists. */ public String getString(String name) throws JSONException { Object object = get(name); String result = JSON.toString(object); if (result == null) { throw JSON.typeMismatch(name, object, "String"); } return result; } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary, or the empty string if no such mapping exists. * * @param name The name of the field we want. * @return The value of the field. */ public String optString(String name) { return optString(name, ""); } /** * Returns the value mapped by {@code name} if it exists, coercing it if * necessary, or {@code fallback} if no such mapping exists. * * @param name The name of the field that we want. * @param fallback The value to return if the field doesn't exist. * @return The value of the field or fallback. */ public String optString(String name, String fallback) { Object object = opt(name); String result = JSON.toString(object); return result != null ? result : fallback; } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONArray}, or throws otherwise. * * @param name The field we want to get. * @return The value of the field (if it is a JSONArray. * @throws JSONException if the mapping doesn't exist or is not a {@code * JSONArray}. */ public JSONArray getJSONArray(String name) throws JSONException { Object object = get(name); if (object instanceof JSONArray) { return (JSONArray) object; } else { throw JSON.typeMismatch(name, object, "JSONArray"); } } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONArray}, or null otherwise. * * @param name The name of the field we want. * @return The value of the specified field (assuming it is a JSNOArray */ public JSONArray optJSONArray(String name) { Object object = opt(name); return object instanceof JSONArray ? (JSONArray) object : null; } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONObject}, or throws otherwise. * * @param name The name of the field that we want. * @return a specified field value (if it is a JSONObject) * @throws JSONException if the mapping doesn't exist or is not a {@code * JSONObject}. */ public JSONObject getJSONObject(String name) throws JSONException { Object object = get(name); if (object instanceof JSONObject) { return (JSONObject) object; } else { throw JSON.typeMismatch(name, object, "JSONObject"); } } /** * Returns the value mapped by {@code name} if it exists and is a {@code * JSONObject}, or null otherwise. * * @param name The name of the value we want. * @return The specified value. */ public JSONObject optJSONObject(String name) { Object object = opt(name); return object instanceof JSONObject ? (JSONObject) object : null; } /** * Returns an array with the values corresponding to {@code names}. The * array contains null for names that aren't mapped. This method returns * null if {@code names} is either null or empty. * * @param names The names of the fields that we want the values for. * @return The selected values. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONArray toJSONArray(JSONArray names) throws JSONException { JSONArray result = new JSONArray(); if (names == null) { return null; } int length = names.length(); if (length == 0) { return null; } for (int i = 0; i < length; i++) { String name = JSON.toString(names.opt(i)); result.put(opt(name)); } return result; } /** * Returns an iterator of the {@code String} names in this object. The * returned iterator supports {@link Iterator#remove() remove}, which will * remove the corresponding mapping from this object. If this object is * modified after the iterator is returned, the iterator's behavior is * undefined. The order of the keys is undefined. * * @return an iterator over the keys. */ public Iterator keys() { return nameValuePairs.keySet().iterator(); } /** * Returns the set of {@code String} names in this object. The returned set * is a view of the keys in this object. {@link Set#remove(Object)} will remove * the corresponding mapping from this object and set iterator behaviour * is undefined if this object is modified after it is returned. * * See {@link #keys()}. * * @return The names in this object. */ public Set keySet() { return nameValuePairs.keySet(); } /** * Returns an array containing the string names in this object. This method * returns null if this object contains no mappings. * * @return the names. */ public JSONArray names() { return nameValuePairs.isEmpty() ? null : new JSONArray(new ArrayList(nameValuePairs.keySet())); } /** * Encodes this object as a compact JSON string, such as: *

{"query":"Pizza","locations":[94043,90210]}
* * Note 1: this method will not output any fields with 'null' value. * Override {@link JSONStringer#entry} method to have nulls printed. * * Note 2: this method will suppress any internal exceptions. * Use {@link JSONObject#toString(JSONStringer)} method directly to handle exceptions manually. */ @Override public String toString() { try { return toString(new JSONStringer()); } catch (JSONException e) { return null; } } /** * Encodes this object as a human readable JSON string for debugging, such * as: *
     * {
     *     "query": "Pizza",
     *     "locations": [
     *         94043,
     *         90210
     *     ]
     * }
* * @param indentSpaces the number of spaces to indent for each level of * nesting. * @return The string containing the pretty form of this. * @throws JSONException On internal errors. Shouldn't happen. */ public String toString(int indentSpaces) throws JSONException { return toString(new JSONStringer(indentSpaces)); } /** * Encodes this object using {@link JSONStringer} provided * * @param stringer - {@link JSONStringer} to be used for serialization * @return The string representation of this. * @throws JSONException On internal errors. Shouldn't happen. */ public String toString(JSONStringer stringer) throws JSONException { encode(stringer); return stringer.toString(); } /** * Encodes this object using {@link JSONStringer} provided * * @param stringer - {@link JSONStringer} to be used for serialization * @throws JSONException On internal errors. Shouldn't happen. */ protected void encode(JSONStringer stringer) throws JSONException { stringer.object(); for (Map.Entry entry : nameValuePairs.entrySet()) { stringer.entry(entry); } stringer.endObject(); } /** * Encodes the number as a JSON string. * * @param number a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return The encoded number in string form. * @throws JSONException On internal errors. Shouldn't happen. */ public static String numberToString(Number number) throws JSONException { if (number == null) { throw new JSONException("Number must be non-null"); } double doubleValue = number.doubleValue(); JSON.checkDouble(doubleValue); // the original returns "-0" instead of "-0.0" for negative zero if (number.equals(NEGATIVE_ZERO)) { return "-0"; } long longValue = number.longValue(); if (doubleValue == longValue) { return Long.toString(longValue); } return number.toString(); } /** * Encodes {@code data} as a JSON string. This applies quotes and any * necessary character escaping. * * @param data the string to encode. Null will be interpreted as an empty * string. * @return the quoted string. */ public static String quote(String data) { if (data == null) { return "\"\""; } try { JSONStringer stringer = new JSONStringer(); stringer.open(JSONStringer.Scope.NULL, ""); stringer.value(data); stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); return stringer.toString(); } catch (JSONException e) { throw new AssertionError(); } } /** * Wraps the given object if necessary. * *

If the object is null or , returns {@link #NULL}. * If the object is a {@code JSONArray} or {@code JSONObject}, no wrapping is necessary. * If the object is {@code NULL}, no wrapping is necessary. * If the object is an array or {@code Collection}, returns an equivalent {@code JSONArray}. * If the object is a {@code Map}, returns an equivalent {@code JSONObject}. * If the object is a primitive wrapper type or {@code String}, returns the object. * If the object is from a {@code java} package, returns the result of {@code toString}. * If the object is some other kind of object then it is assumed to be a bean and is converted to a JSONObject. * If wrapping fails, returns null. * * @param o The object to wrap. * @return The wrapped (if necessary) form of the object {$code o} */ public static Object wrap(Object o) { if (o == null) { return NULL; } if (o instanceof JSONArray || o instanceof JSONObject) { return o; } if (o.equals(NULL)) { return o; } try { if (o instanceof Collection) { return new JSONArray((Collection) o); } else if (o.getClass().isArray()) { return new JSONArray(o); } if (o instanceof Map) { return new JSONObject((Map) o); } if (o instanceof Boolean || o instanceof Byte || o instanceof Character || o instanceof Double || o instanceof Float || o instanceof Integer || o instanceof Long || o instanceof Short || o instanceof String || o instanceof JSONString) { return o; } if (o.getClass().getPackage().getName().startsWith("java.") || o instanceof Enum) { return o.toString(); } else { return new JSONObject(o); } } catch (Exception ignored) { } return null; } } openjson-1.0.12/src/main/java/com/github/openjson/JSONString.java000066400000000000000000000013101363767403400245700ustar00rootroot00000000000000package com.github.openjson; /** * The JSONString interface allows a toJSONString() * method so that a class can change the behavior of * JSONObject.toString(), JSONArray.toString(), * and JSONWriter.value(Object). The * toJSONString method will be used instead of the default behavior * of using the Object's toString() method and quoting the result. */ public interface JSONString { /** * The toJSONString method allows a class to produce its own JSON * serialization. * * @return A strictly syntactically correct JSON text. */ String toJSONString(); }openjson-1.0.12/src/main/java/com/github/openjson/JSONStringer.java000066400000000000000000000372471363767403400251410ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; // Note: this class was written without inspecting the non-free org.json sourcecode. /** * Implements {@link JSONObject#toString} and {@link JSONArray#toString}. Most * application developers should use those methods directly and disregard this * API. For example:

 * JSONObject object = ...
 * String json = object.toString();
*
*

Stringers only encode well-formed JSON strings. In particular: *

    *
  • The stringer must have exactly one top-level array or object. *
  • Lexical scopes must be balanced: every call to {@link #array} must * have a matching call to {@link #endArray} and every call to {@link * #object} must have a matching call to {@link #endObject}. *
  • Arrays may not contain keys (property names). *
  • Objects must alternate keys (property names) and values. *
  • Values are inserted with either literal {@link #value(Object) value} * calls, or by nesting arrays or objects. *
* Calls that would result in a malformed JSON string will fail with a * {@link JSONException}. *
*

This class provides no facility for pretty-printing (ie. indenting) * output. To encode indented output, use {@link JSONObject#toString(int)} or * {@link JSONArray#toString(int)}. *
*

Some implementations of the API support at most 20 levels of nesting. * Attempts to create more than 20 levels of nesting may fail with a {@link * JSONException}. *
*

Each stringer may be used to encode a single top level value. Instances of * this class are not thread safe. Although this class is nonfinal, it was not * designed for inheritance and should not be subclassed. In particular, * self-use by overrideable methods is not specified. See Effective Java * Item 17, "Design and Document or inheritance or else prohibit it" for further * information. */ public class JSONStringer { /** * The output data, containing at most one top-level array or object. */ final protected StringBuilder out = new StringBuilder(); /** * Lexical scoping elements within this stringer, necessary to insert the * appropriate separator characters (ie. commas and colons) and to detect * nesting errors. */ enum Scope { /** * An array with no elements requires no separators or newlines before * it is closed. */ EMPTY_ARRAY, /** * A array with at least one value requires a comma and newline before * the next element. */ NONEMPTY_ARRAY, /** * An object with no keys or values requires no separators or newlines * before it is closed. */ EMPTY_OBJECT, /** * An object whose most recent element is a key. The next element must * be a value. */ DANGLING_KEY, /** * An object with at least one name/value pair requires a comma and * newline before the next element. */ NONEMPTY_OBJECT, /** * A special bracketless array needed by JSONStringer.join() and * JSONObject.quote() only. Not used for JSON encoding. */ NULL, } /** * Unlike the original implementation, this stack isn't limited to 20 * levels of nesting. */ private final List stack = new ArrayList(); /** * A string containing a full set of spaces for a single level of * indentation, or null for no pretty printing. */ private final String indent; public JSONStringer() { this(0); } public JSONStringer(int indentSpaces) { if (indentSpaces > 0) { char[] indentChars = new char[indentSpaces]; Arrays.fill(indentChars, ' '); indent = new String(indentChars); } else { indent = null; } } /** * Begins encoding a new array. Each call to this method must be paired with * a call to {@link #endArray}. * * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer array() throws JSONException { return open(Scope.EMPTY_ARRAY, "["); } /** * Ends encoding the current array. * * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer endArray() throws JSONException { return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]"); } /** * Begins encoding a new object. Each call to this method must be paired * with a call to {@link #endObject}. * * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer object() throws JSONException { return open(Scope.EMPTY_OBJECT, "{"); } /** * Ends encoding the current object. * * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer endObject() throws JSONException { return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}"); } /** * Enters a new scope by appending any necessary whitespace and the given * bracket. */ JSONStringer open(Scope empty, String openBracket) throws JSONException { if (stack.isEmpty() && out.length() > 0) { throw new JSONException("Nesting problem: multiple top-level roots"); } beforeValue(); stack.add(empty); out.append(openBracket); return this; } /** * Closes the current scope by appending any necessary whitespace and the * given bracket. */ JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException { Scope context = peek(); if (context != nonempty && context != empty) { throw new JSONException("Nesting problem"); } stack.remove(stack.size() - 1); if (context == nonempty) { newline(); } out.append(closeBracket); return this; } /** * Returns the value on the top of the stack. */ private Scope peek() throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } return stack.get(stack.size() - 1); } /** * Replace the value on the top of the stack with the given value. */ private void replaceTop(Scope topOfStack) { stack.set(stack.size() - 1, topOfStack); } /** * Encodes {@code value}. * * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs} * or {@link Double#isInfinite() infinities}. * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer value(Object value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } if (value instanceof JSONArray) { ((JSONArray) value).encode(this); return this; } else if (value instanceof JSONObject) { ((JSONObject) value).encode(this); return this; } beforeValue(); if (value instanceof JSONString) { out.append(((JSONString) value).toJSONString()); return this; } if (value == null || value instanceof Boolean || value == JSONObject.NULL) { out.append(value); } else if (value instanceof Number) { out.append(JSONObject.numberToString((Number) value)); } else { // Hack to make it possible that the value is not surrounded by quotes. (Used for JavaScript function calls) // Example: { "name": "testkey", "value": window.myfunction() } if (value.getClass().getName().contains("JSONFunction")) { // note that no escaping of quotes (or anything else) is done in this case. // that is fine because the only way to get to this point is to // explicitly put a special kind of object into the JSON data structure. out.append(value); } else { string(value.toString()); } } return this; } /** * Encodes {@code value} to this stringer. * * @param value The value to encode. * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer value(boolean value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); out.append(value); return this; } /** * Encodes {@code value} to this stringer. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or * {@link Double#isInfinite() infinities}. * @return this stringer. * @throws JSONException On internal errors. Shouldn't happen. */ public JSONStringer value(double value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); out.append(JSONObject.numberToString(value)); return this; } /** * Encodes {@code value} to this stringer. * * @param value The value to encode. * @return this stringer. * @throws JSONException If we have an internal error. Shouldn't happen. */ public JSONStringer value(long value) throws JSONException { if (stack.isEmpty()) { throw new JSONException("Nesting problem"); } beforeValue(); out.append(value); return this; } /** * Encodes {@code key}/{@code value} pair to this stringer. * * @param entry The entry to encode. * @return this stringer. * @throws JSONException If we have an internal error. Shouldn't happen. */ public JSONStringer entry(Map.Entry entry) { if (!JSONObject.NULL.equals(entry.getValue())) { this.key(entry.getKey()).value(entry.getValue()); } return this; } private void string(String value) { out.append("\""); char currentChar = 0; for (int i = 0, length = value.length(); i < length; i++) { char previousChar = currentChar; currentChar = value.charAt(i); /* * From RFC 4627, "All Unicode characters may be placed within the * quotation marks except for the characters that must be escaped: * quotation mark, reverse solidus, and the control characters * (U+0000 through U+001F)." */ switch (currentChar) { case '"': case '\\': out.append('\\').append(currentChar); break; case '/': // it makes life easier for HTML embedding of javascript if we escape *

If invoked with unterminated arrays or unclosed objects, this method's * return value is undefined. *
*

Warning: although it contradicts the general contract * of {@link Object#toString}, this method returns null if the stringer * contains no data. */ @Override public String toString() { return out.length() == 0 ? null : out.toString(); } } openjson-1.0.12/src/main/java/com/github/openjson/JSONTokener.java000066400000000000000000000532031363767403400247410ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; // Note: this class was written without inspecting the non-free org.json sourcecode. import java.io.IOException; import java.io.Reader; /** * Parses a JSON (RFC 4627) * encoded string into the corresponding object. Most clients of * this class will use only need the {@link #JSONTokener(String) constructor} * and {@link #nextValue} method. Example usage:

 * String json = "{"
 *         + "  \"query\": \"Pizza\", "
 *         + "  \"locations\": [ 94043, 90210 ] "
 *         + "}";
 *
 * JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
 * String query = object.getString("query");
 * JSONArray locations = object.getJSONArray("locations");
* *

For best interoperability and performance use JSON that complies with * RFC 4627, such as that generated by {@link JSONStringer}. For legacy reasons * this parser is lenient, so a successful parse does not indicate that the * input string was valid JSON. All of the following syntax errors will be * ignored: *

    *
  • End of line comments starting with {@code //} or {@code #} and ending * with a newline character. *
  • C-style comments starting with {@code /*} and ending with * {@code *}{@code /}. Such comments may not be nested. *
  • Strings that are unquoted or {@code 'single quoted'}. *
  • Hexadecimal integers prefixed with {@code 0x} or {@code 0X}. *
  • Octal integers prefixed with {@code 0}. *
  • Array elements separated by {@code ;}. *
  • Unnecessary array separators. These are interpreted as if null was the * omitted value. *
  • Key-value pairs separated by {@code =} or {@code =>}. *
  • Key-value pairs separated by {@code ;}. *
* *

Each tokener may be used to parse a single JSON string. Instances of this * class are not thread safe. Although this class is nonfinal, it was not * designed for inheritance and should not be subclassed. In particular, * self-use by overrideable methods is not specified. See Effective Java * Item 17, "Design and Document or inheritance or else prohibit it" for further * information. */ public class JSONTokener { /** * The input JSON. */ private final String in; /** * The index of the next character to be returned by {@link #next}. When * the input is exhausted, this equals the input's length. */ private int pos; /** * @param in JSON encoded string. Null is not permitted and will yield a * tokener that throws {@code NullPointerExceptions} when methods are * called. */ public JSONTokener(String in) { // consume an optional byte order mark (BOM) if it exists if (in != null && in.startsWith("\ufeff")) { in = in.substring(1); } this.in = in; } public JSONTokener(Reader input) throws IOException { StringBuilder s = new StringBuilder(); char[] readBuf = new char[102400]; int n = input.read(readBuf); while (n >= 0) { s.append(readBuf, 0, n); n = input.read(readBuf); } in = s.toString(); pos = 0; } /** * Returns the next value from the input. * * @return a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Integer, Long, Double or {@link JSONObject#NULL}. * @throws JSONException if the input is malformed. */ public Object nextValue() throws JSONException { int c = nextCleanInternal(); switch (c) { case -1: throw syntaxError("End of input"); case '{': return readObject(); case '[': return readArray(); case '\'': case '"': return nextString((char) c); default: pos--; return readLiteral(); } } private int nextCleanInternal() throws JSONException { while (pos < in.length()) { int c = in.charAt(pos++); switch (c) { case '\t': case ' ': case '\n': case '\r': continue; case '/': if (pos == in.length()) { return c; } char peek = in.charAt(pos); switch (peek) { case '*': // skip a /* c-style comment */ pos++; int commentEnd = in.indexOf("*/", pos); if (commentEnd == -1) { throw syntaxError("Unterminated comment"); } pos = commentEnd + 2; continue; case '/': // skip a // end-of-line comment pos++; skipToEndOfLine(); continue; default: return c; } case '#': /* * Skip a # hash end-of-line comment. The JSON RFC doesn't * specify this behavior, but it's required to parse * existing documents. See http://b/2571423. */ skipToEndOfLine(); continue; default: return c; } } return -1; } /** * Advances the position until after the next newline character. If the line * is terminated by "\r\n", the '\n' must be consumed as whitespace by the * caller. */ private void skipToEndOfLine() { for (; pos < in.length(); pos++) { char c = in.charAt(pos); if (c == '\r' || c == '\n') { pos++; break; } } } /** * Returns the string up to but not including {@code quote}, unescaping any * character escape sequences encountered along the way. The opening quote * should have already been read. This consumes the closing quote, but does * not include it in the returned string. * * @param quote either ' or ". * @return The unescaped string. * @throws JSONException if the string isn't terminated by a closing quote correctly. */ public String nextString(char quote) throws JSONException { /* * For strings that are free of escape sequences, we can just extract * the result as a substring of the input. But if we encounter an escape * sequence, we need to use a StringBuilder to compose the result. */ StringBuilder builder = null; /* the index of the first character not yet appended to the builder. */ int start = pos; while (pos < in.length()) { int c = in.charAt(pos++); if (c == quote) { if (builder == null) { // a new string avoids leaking memory return new String(in.substring(start, pos - 1)); } else { builder.append(in, start, pos - 1); return builder.toString(); } } if (c == '\\') { if (pos == in.length()) { throw syntaxError("Unterminated escape sequence"); } if (builder == null) { builder = new StringBuilder(); } builder.append(in, start, pos - 1); builder.append(readEscapeCharacter()); start = pos; } } throw syntaxError("Unterminated string"); } /** * Unescapes the character identified by the character or characters that * immediately follow a backslash. The backslash '\' should have already * been read. This supports both unicode escapes "u000A" and two-character * escapes "\n". */ private char readEscapeCharacter() throws JSONException { char escaped = in.charAt(pos++); switch (escaped) { case 'u': if (pos + 4 > in.length()) { throw syntaxError("Unterminated escape sequence"); } String hex = in.substring(pos, pos + 4); pos += 4; try { return (char) Integer.parseInt(hex, 16); } catch (NumberFormatException nfe) { throw syntaxError("Invalid escape sequence: " + hex); } case 't': return '\t'; case 'b': return '\b'; case 'n': return '\n'; case 'r': return '\r'; case 'f': return '\f'; case '\'': case '"': case '\\': default: return escaped; } } /** * Reads a null, boolean, numeric or unquoted string literal value. Numeric * values will be returned as an Integer, Long, or Double, in that order of * preference. */ private Object readLiteral() throws JSONException { String literal = nextToInternal("{}[]/\\:,=;# \t\f"); if (literal.length() == 0) { throw syntaxError("Expected literal value"); } else if ("null".equalsIgnoreCase(literal)) { return JSONObject.NULL; } else if ("true".equalsIgnoreCase(literal)) { return Boolean.TRUE; } else if ("false".equalsIgnoreCase(literal)) { return Boolean.FALSE; } /* try to parse as an integral type... */ if (literal.indexOf('.') == -1) { int base = 10; String number = literal; if (number.startsWith("0x") || number.startsWith("0X")) { number = number.substring(2); base = 16; } else if (number.startsWith("0") && number.length() > 1) { number = number.substring(1); base = 8; } try { long longValue = Long.parseLong(number, base); if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { return (int) longValue; } else { return longValue; } } catch (NumberFormatException e) { /* * This only happens for integral numbers greater than * Long.MAX_VALUE, numbers in exponential form (5e-10) and * unquoted strings. Fall through to try floating point. */ } } /* ...next try to parse as a floating point... */ try { return Double.valueOf(literal); } catch (NumberFormatException ignored) { } /* ... finally give up. We have an unquoted string */ //noinspection RedundantStringConstructorCall return new String(literal); // a new string avoids leaking memory } /** * Returns the string up to but not including any of the given characters or * a newline character. This does not consume the excluded character. */ private String nextToInternal(String excluded) { int start = pos; for (; pos < in.length(); pos++) { char c = in.charAt(pos); if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) { return in.substring(start, pos); } } return in.substring(start); } /** * Reads a sequence of key/value pairs and the trailing closing brace '}' of * an object. The opening brace '{' should have already been read. */ private JSONObject readObject() throws JSONException { JSONObject result = new JSONObject(); /* Peek to see if this is the empty object. */ int first = nextCleanInternal(); if (first == '}') { return result; } else if (first != -1) { pos--; } while (true) { Object name = nextValue(); if (!(name instanceof String)) { if (name == null) { throw syntaxError("Names cannot be null"); } else { throw syntaxError("Names must be strings, but " + name + " is of type " + name.getClass().getName()); } } /* * Expect the name/value separator to be either a colon ':', an * equals sign '=', or an arrow "=>". The last two are bogus but we * include them because that's what the original implementation did. */ int separator = nextCleanInternal(); if (separator != ':' && separator != '=') { throw syntaxError("Expected ':' after " + name); } if (pos < in.length() && in.charAt(pos) == '>') { pos++; } result.put((String) name, nextValue()); switch (nextCleanInternal()) { case '}': return result; case ';': case ',': continue; default: throw syntaxError("Unterminated object"); } } } /** * Reads a sequence of values and the trailing closing brace ']' of an * array. The opening brace '[' should have already been read. Note that * "[]" yields an empty array, but "[,]" returns a two-element array * equivalent to "[null,null]". */ private JSONArray readArray() throws JSONException { JSONArray result = new JSONArray(); /* to cover input that ends with ",]". */ boolean hasTrailingSeparator = false; while (true) { switch (nextCleanInternal()) { case -1: throw syntaxError("Unterminated array"); case ']': if (hasTrailingSeparator) { result.put(null); } return result; case ',': case ';': /* A separator without a value first means "null". */ result.put(null); hasTrailingSeparator = true; continue; default: pos--; } result.put(nextValue()); switch (nextCleanInternal()) { case ']': return result; case ',': case ';': hasTrailingSeparator = true; continue; default: throw syntaxError("Unterminated array"); } } } /** * Returns an exception containing the given message plus the current * position and the entire input string. * * @param message The message we want to include. * @return An exception that we can throw. */ public JSONException syntaxError(String message) { return new JSONException(message + this); } /** * Returns the current position and the entire input string. */ @Override public String toString() { // consistent with the original implementation return " at character " + pos + " of " + in; } /* * Legacy APIs. * * None of the methods below are on the critical path of parsing JSON * documents. They exist only because they were exposed by the original * implementation and may be used by some clients. */ /** * Returns true until the input has been exhausted. * * @return true if more input exists. */ public boolean more() { return pos < in.length(); } /** * Returns the next available character, or the null character '\0' if all * input has been exhausted. The return value of this method is ambiguous * for JSON strings that contain the character '\0'. * * @return the next character. */ public char next() { return pos < in.length() ? in.charAt(pos++) : '\0'; } /** * Returns the next available character if it equals {@code c}. Otherwise an * exception is thrown. * * @param c The character we are looking for. * @return the next character. * @throws JSONException If the next character isn't {@code c} */ public char next(char c) throws JSONException { char result = next(); if (result != c) { throw syntaxError("Expected " + c + " but was " + result); } return result; } /** * Returns the next character that is not whitespace and does not belong to * a comment. If the input is exhausted before such a character can be * found, the null character '\0' is returned. The return value of this * method is ambiguous for JSON strings that contain the character '\0'. * * @return The next non-whitespace character. * @throws JSONException Should not be possible. */ public char nextClean() throws JSONException { int nextCleanInt = nextCleanInternal(); return nextCleanInt == -1 ? '\0' : (char) nextCleanInt; } /** * Returns the next {@code length} characters of the input. * *

The returned string shares its backing character array with this * tokener's input string. If a reference to the returned string may be held * indefinitely, you should use {@code new String(result)} to copy it first * to avoid memory leaks. * * @param length The desired number of characters to return. * @return The next few characters. * @throws JSONException if the remaining input is not long enough to * satisfy this request. */ public String next(int length) throws JSONException { if (pos + length > in.length()) { throw syntaxError(length + " is out of bounds"); } String result = in.substring(pos, pos + length); pos += length; return result; } /** * Returns the {@link String#trim trimmed} string holding the characters up * to but not including the first of: *

    *
  • any character in {@code excluded} *
  • a newline character '\n' *
  • a carriage return '\r' *
* *

The returned string shares its backing character array with this * tokener's input string. If a reference to the returned string may be held * indefinitely, you should use {@code new String(result)} to copy it first * to avoid memory leaks. * * @param excluded The limiting string where the search should stop. * @return a possibly-empty string */ public String nextTo(String excluded) { if (excluded == null) { throw new NullPointerException("excluded == null"); } return nextToInternal(excluded).trim(); } /** * Equivalent to {@code nextTo(String.valueOf(excluded))}. * * @param excluded The limiting character. * @return a possibly-empty string */ public String nextTo(char excluded) { return nextToInternal(String.valueOf(excluded)).trim(); } /** * Advances past all input up to and including the next occurrence of * {@code thru}. If the remaining input doesn't contain {@code thru}, the * input is exhausted. * * @param thru The string to skip over. */ public void skipPast(String thru) { int thruStart = in.indexOf(thru, pos); pos = thruStart == -1 ? in.length() : (thruStart + thru.length()); } /** * Advances past all input up to but not including the next occurrence of * {@code to}. If the remaining input doesn't contain {@code to}, the input * is unchanged. * * @param to The character we want to skip to. * @return The value of {@code to} or null. */ public char skipTo(char to) { int index = in.indexOf(to, pos); if (index != -1) { pos = index; return to; } else { return '\0'; } } /** * Unreads the most recent character of input. If no input characters have * been read, the input is unchanged. */ public void back() { if (--pos == -1) { pos = 0; } } /** * Returns the integer [0..15] value for the given hex character, or -1 * for non-hex input. * * @param hex a character in the ranges [0-9], [A-F] or [a-f]. Any other * character will yield a -1 result. * @return The decoded integer. */ public static int dehexchar(char hex) { if (hex >= '0' && hex <= '9') { return hex - '0'; } else if (hex >= 'A' && hex <= 'F') { return hex - 'A' + 10; } else if (hex >= 'a' && hex <= 'f') { return hex - 'a' + 10; } else { return -1; } } } openjson-1.0.12/src/test/000077500000000000000000000000001363767403400152115ustar00rootroot00000000000000openjson-1.0.12/src/test/java/000077500000000000000000000000001363767403400161325ustar00rootroot00000000000000openjson-1.0.12/src/test/java/com/000077500000000000000000000000001363767403400167105ustar00rootroot00000000000000openjson-1.0.12/src/test/java/com/github/000077500000000000000000000000001363767403400201725ustar00rootroot00000000000000openjson-1.0.12/src/test/java/com/github/openjson/000077500000000000000000000000001363767403400220255ustar00rootroot00000000000000openjson-1.0.12/src/test/java/com/github/openjson/FileTest.java000066400000000000000000000337671363767403400244270ustar00rootroot00000000000000package com.github.openjson; import org.junit.Assert; import org.junit.Test; import java.io.IOException; import java.io.InputStreamReader; import java.util.Set; public class FileTest { @Test public void testFile() throws IOException, JSONException { String ref = "[\n" + " {\n" + " \"_id\": \"58309f3bd307b72ae49a9b23\",\n" + " \"index\": 0,\n" + " \"guid\": \"5764ebd8-b333-469e-8d83-4eb5658f1566\",\n" + " \"isActive\": true,\n" + " \"balance\": \"$1,099.93\",\n" + " \"picture\": \"http://placehold.it/32x32\",\n" + " \"age\": 37,\n" + " \"eyeColor\": \"blue\",\n" + " \"name\": \"Barrera Wilkerson\",\n" + " \"gender\": \"male\",\n" + " \"company\": \"VURBO\",\n" + " \"email\": \"barrerawilkerson@vurbo.com\",\n" + " \"phone\": \"+1 (817) 429-2473\",\n" + " \"address\": \"522 Vanderveer Street, Detroit, Wyoming, 4320\",\n" + " \"about\": \"Et officia aute ullamco magna adipisicing non ut cupidatat cupidatat aliquip. Tempor occaecat ex ad dolore aliquip mollit ea esse ipsum. Est incididunt sunt commodo duis est. Reprehenderit in ut reprehenderit ad culpa ea fugiat et est adipisicing aliquip. Id mollit voluptate qui pariatur officia.\\r\\n\",\n" + " \"registered\": \"2016-06-29T08:54:14 +07:00\",\n" + " \"latitude\": -87.548434,\n" + " \"longitude\": 64.251242,\n" + " \"tags\": [\n" + " \"aliqua\",\n" + " \"ex\",\n" + " \"sit\",\n" + " \"magna\",\n" + " \"dolor\",\n" + " \"laborum\",\n" + " \"non\"\n" + " ],\n" + " \"friends\": [\n" + " {\n" + " \"id\": 0,\n" + " \"name\": \"Byers Pratt\"\n" + " },\n" + " {\n" + " \"id\": 1,\n" + " \"name\": \"Kennedy Contreras\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"name\": \"Frazier Monroe\"\n" + " }\n" + " ],\n" + " \"greeting\": \"Hello, Barrera Wilkerson! You have 3 unread messages.\",\n" + " \"favoriteFruit\": \"banana\"\n" + " },\n" + " {\n" + " \"_id\": \"58309f3b1f506440093a41d1\",\n" + " \"index\": 1,\n" + " \"guid\": \"de1a6cc9-f8b3-426e-b68a-cc30e1fff3c1\",\n" + " \"isActive\": false,\n" + " \"balance\": \"$3,397.60\",\n" + " \"picture\": \"http://placehold.it/32x32\",\n" + " \"age\": 32,\n" + " \"eyeColor\": \"blue\",\n" + " \"name\": \"Trisha Morris\",\n" + " \"gender\": \"female\",\n" + " \"company\": \"AMTAP\",\n" + " \"email\": \"trishamorris@amtap.com\",\n" + " \"phone\": \"+1 (805) 423-3375\",\n" + " \"address\": \"495 Tampa Court, Libertytown, New Hampshire, 5177\",\n" + " \"about\": \"Elit culpa Lorem dolor sit laborum ut ullamco ullamco nostrud reprehenderit adipisicing eiusmod. Aliqua quis dolor esse sint. Dolore in excepteur laborum anim ut consectetur. Nisi officia est eu ex ex id. Ipsum duis ullamco ad ut labore dolor. In amet tempor deserunt ullamco velit eu fugiat.\\r\\n\",\n" + " \"registered\": \"2015-02-08T06:14:19 +08:00\",\n" + " \"latitude\": -81.956277,\n" + " \"longitude\": 143.685584,\n" + " \"tags\": [\n" + " \"cillum\",\n" + " \"ullamco\",\n" + " \"magna\",\n" + " \"cillum\",\n" + " \"voluptate\",\n" + " \"magna\",\n" + " \"exercitation\"\n" + " ],\n" + " \"friends\": [\n" + " {\n" + " \"id\": 0,\n" + " \"name\": \"Fuentes Stout\"\n" + " },\n" + " {\n" + " \"id\": 1,\n" + " \"name\": \"Violet Vargas\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"name\": \"Schmidt Wilder\"\n" + " }\n" + " ],\n" + " \"greeting\": \"Hello, Trisha Morris! You have 4 unread messages.\",\n" + " \"favoriteFruit\": \"strawberry\"\n" + " },\n" + " {\n" + " \"_id\": \"58309f3beaef2f31339b3755\",\n" + " \"index\": 2,\n" + " \"guid\": \"0bf387b7-abc2-4828-becc-1269928f7c3d\",\n" + " \"isActive\": false,\n" + " \"balance\": \"$1,520.64\",\n" + " \"picture\": \"http://placehold.it/32x32\",\n" + " \"age\": 37,\n" + " \"eyeColor\": \"blue\",\n" + " \"name\": \"Deanna Santiago\",\n" + " \"gender\": \"female\",\n" + " \"company\": \"MEGALL\",\n" + " \"email\": \"deannasantiago@megall.com\",\n" + " \"phone\": \"+1 (916) 511-2291\",\n" + " \"address\": \"919 Fayette Street, Homestead, Utah, 8669\",\n" + " \"about\": \"Sit amet ex quis velit irure Lorem non quis aliquip dolor pariatur nulla Lorem officia. Deserunt officia sit velit labore sint nostrud elit aliquip labore ullamco consectetur id amet. Ullamco duis commodo sit incididunt. Fugiat consectetur ad incididunt officia. Sint cillum minim laborum laboris id cillum est exercitation in eiusmod qui.\\r\\n\",\n" + " \"registered\": \"2015-11-18T08:39:28 +08:00\",\n" + " \"latitude\": 79.105701,\n" + " \"longitude\": -146.901754,\n" + " \"tags\": [\n" + " \"non\",\n" + " \"ullamco\",\n" + " \"cillum\",\n" + " \"ipsum\",\n" + " \"amet\",\n" + " \"aliqua\",\n" + " \"aliquip\"\n" + " ],\n" + " \"friends\": [\n" + " {\n" + " \"id\": 0,\n" + " \"name\": \"Hanson Anderson\"\n" + " },\n" + " {\n" + " \"id\": 1,\n" + " \"name\": \"Pollard Soto\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"name\": \"Barlow Campbell\"\n" + " }\n" + " ],\n" + " \"greeting\": \"Hello, Deanna Santiago! You have 7 unread messages.\",\n" + " \"favoriteFruit\": \"apple\"\n" + " },\n" + " {\n" + " \"_id\": \"58309f3b49a68ad01346f27f\",\n" + " \"index\": 3,\n" + " \"guid\": \"d29c0dcc-48fb-4ca4-a63b-b47c0e6d6398\",\n" + " \"isActive\": false,\n" + " \"balance\": \"$2,069.96\",\n" + " \"picture\": \"http://placehold.it/32x32\",\n" + " \"age\": 29,\n" + " \"eyeColor\": \"green\",\n" + " \"name\": \"Brooks Gates\",\n" + " \"gender\": \"male\",\n" + " \"company\": \"TERRAGEN\",\n" + " \"email\": \"brooksgates@terragen.com\",\n" + " \"phone\": \"+1 (875) 483-2224\",\n" + " \"address\": \"562 Noll Street, Kipp, Louisiana, 7659\",\n" + " \"about\": \"Reprehenderit laboris mollit nulla commodo quis laborum commodo. Laborum aliquip laboris officia minim ipsum laborum ipsum reprehenderit quis laboris est sint culpa. Culpa magna aute mollit exercitation.\\r\\n\",\n" + " \"registered\": \"2016-05-04T10:34:38 +07:00\",\n" + " \"latitude\": 72.77079,\n" + " \"longitude\": -134.291768,\n" + " \"tags\": [\n" + " \"est\",\n" + " \"sunt\",\n" + " \"laboris\",\n" + " \"ea\",\n" + " \"proident\",\n" + " \"aute\",\n" + " \"excepteur\"\n" + " ],\n" + " \"friends\": [\n" + " {\n" + " \"id\": 0,\n" + " \"name\": \"Roxanne Morgan\"\n" + " },\n" + " {\n" + " \"id\": 1,\n" + " \"name\": \"Tamara Kelly\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"name\": \"Cleveland Bush\"\n" + " }\n" + " ],\n" + " \"greeting\": \"Hello, Brooks Gates! You have 1 unread messages.\",\n" + " \"favoriteFruit\": \"banana\"\n" + " },\n" + " {\n" + " \"_id\": \"58309f3be746700e9af9a645\",\n" + " \"index\": 4,\n" + " \"guid\": \"54382bd6-c476-469d-9e1c-e546f959db51\",\n" + " \"isActive\": true,\n" + " \"balance\": \"$2,012.57\",\n" + " \"picture\": \"http://placehold.it/32x32\",\n" + " \"age\": 40,\n" + " \"eyeColor\": \"brown\",\n" + " \"name\": \"Jackie Thomas\",\n" + " \"gender\": \"female\",\n" + " \"company\": \"HINWAY\",\n" + " \"email\": \"jackiethomas@hinway.com\",\n" + " \"phone\": \"+1 (843) 470-2096\",\n" + " \"address\": \"910 Emerson Place, Gwynn, Federated States Of Micronesia, 4688\",\n" + " \"about\": \"Id cupidatat laboris elit est eiusmod esse nostrud. Ex commodo nisi voluptate est nisi laborum officia sint incididunt pariatur qui deserunt ullamco. Fugiat proident magna ipsum sit sint id adipisicing sit nostrud labore sit officia. Eiusmod exercitation non enim excepteur amet irure ullamco consectetur cupidatat proident Lorem reprehenderit aliquip. Veniam esse dolor Lorem incididunt proident officia enim in incididunt culpa. Mollit voluptate commodo aliquip anim ipsum nostrud ut labore enim labore qui do minim incididunt. Quis irure proident voluptate nisi qui sunt aute duis irure.\\r\\n\",\n" + " \"registered\": \"2014-08-03T09:21:43 +07:00\",\n" + " \"latitude\": 84.871256,\n" + " \"longitude\": 2.043339,\n" + " \"tags\": [\n" + " \"tempor\",\n" + " \"ut\",\n" + " \"deserunt\",\n" + " \"esse\",\n" + " \"nostrud\",\n" + " \"dolore\",\n" + " \"ex\"\n" + " ],\n" + " \"friends\": [\n" + " {\n" + " \"id\": 0,\n" + " \"name\": \"Lois Walters\"\n" + " },\n" + " {\n" + " \"id\": 1,\n" + " \"name\": \"Brewer Buchanan\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"name\": \"Mccormick Fleming\"\n" + " }\n" + " ],\n" + " \"greeting\": \"Hello, Jackie Thomas! You have 2 unread messages.\",\n" + " \"favoriteFruit\": \"banana\"\n" + " }\n" + "]"; JSONArray x1 = (JSONArray) new JSONTokener(new InputStreamReader(this.getClass().getResourceAsStream("/sample-01.json"))).nextValue(); JSONArray x2 = (JSONArray) new JSONTokener(ref).nextValue(); Assert.assertTrue(jsonEquals(x1, x2)); } private boolean jsonEquals(JSONArray x1, JSONArray x2) throws JSONException { if (x1.length() != x2.length()) { return false; } for (int i = 0; i < x1.length(); i++) { Object element1 = x1.get(i); Object element2 = x2.get(i); if (!jsonEquals(element1, element2)) { return false; } } return true; } private boolean jsonEquals(JSONObject x1, JSONObject x2) throws JSONException { if (x1.length() != x2.length()) { return false; } Set names = x1.keySet(); for (String name : names) { if (!jsonEquals(x1.get(name), x2.get(name))) { return false; } } return true; } private boolean jsonEquals(Object element1, Object element2) throws JSONException { if (!element1.getClass().equals(element2.getClass())) { return false; } if (element1 instanceof JSONObject) { return jsonEquals((JSONObject) element1, (JSONObject) element2); } if (element1 instanceof JSONArray) { return jsonEquals((JSONArray) element1, (JSONArray) element2); } return element1.equals(element2); } } openjson-1.0.12/src/test/java/com/github/openjson/JSONArrayTest.java000066400000000000000000000574421363767403400253140ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import org.junit.Test; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * This black box test was written without inspecting the non-free org.json sourcecode. */ public class JSONArrayTest { @Test public void testEmptyArray() throws JSONException { JSONArray array = new JSONArray(); assertEquals(0, array.length()); assertEquals("", array.join(" AND ")); try { array.get(0); fail(); } catch (JSONException ignored) { } try { array.getBoolean(0); fail(); } catch (JSONException ignored) { } assertEquals("[]", array.toString()); assertEquals("[]", array.toString(4)); // out of bounds is co-opted with defaulting assertTrue(array.isNull(0)); assertNull(array.opt(0)); assertFalse(array.optBoolean(0)); assertTrue(array.optBoolean(0, true)); // bogus (but documented) behaviour: returns null rather than an empty object! assertNull(array.toJSONObject(new JSONArray())); } @Test public void testEqualsAndHashCode() throws JSONException { JSONArray a = new JSONArray(); JSONArray b = new JSONArray(); assertEquals(a, b); assertEquals("equals() not consistent with hashCode()", a.hashCode(), b.hashCode()); a.put(true); a.put(false); b.put(true); b.put(false); assertEquals(a, b); assertEquals(a.hashCode(), b.hashCode()); b.put(true); assertNotEquals(a, b); assertTrue(a.hashCode() != b.hashCode()); } @Test public void testBooleans() throws JSONException { JSONArray array = new JSONArray(); array.put(true); array.put(false); array.put(2, false); array.put(3, false); array.put(2, true); assertEquals("[true,false,true,false]", array.toString()); assertEquals(4, array.length()); assertEquals(Boolean.TRUE, array.get(0)); assertEquals(Boolean.FALSE, array.get(1)); assertEquals(Boolean.TRUE, array.get(2)); assertEquals(Boolean.FALSE, array.get(3)); assertFalse(array.isNull(0)); assertFalse(array.isNull(1)); assertFalse(array.isNull(2)); assertFalse(array.isNull(3)); assertTrue(array.optBoolean(0)); assertFalse(array.optBoolean(1, true)); assertTrue(array.optBoolean(2, false)); assertFalse(array.optBoolean(3)); assertEquals("true", array.getString(0)); assertEquals("false", array.getString(1)); assertEquals("true", array.optString(2)); assertEquals("false", array.optString(3, "x")); assertEquals("[\n true,\n false,\n true,\n false\n]", array.toString(5)); JSONArray other = new JSONArray(); other.put(true); other.put(false); other.put(true); other.put(false); assertEquals(array, other); other.put(true); assertNotEquals(array, other); other = new JSONArray(); other.put("true"); other.put("false"); other.put("truE"); other.put("FALSE"); assertNotEquals(array, other); assertNotEquals(other, array); assertTrue(other.getBoolean(0)); assertFalse(other.optBoolean(1, true)); assertTrue(other.optBoolean(2)); assertFalse(other.getBoolean(3)); } @Test public void testCoerceStringToNumber() throws JSONException { JSONArray array = new JSONArray(); array.put("1"); array.put("-1"); assertEquals(1, array.getInt(0)); assertEquals(-1, array.getInt(1)); } @Test public void testCoerceStringToNumberWithFail() throws JSONException { JSONArray array = new JSONArray(); array.put("not a number"); try { array.getLong(0); fail(); } catch (JSONException ignored) { } assertEquals(0, array.optLong(0)); } // http://code.google.com/p/android/issues/detail?id=16411 @Test public void testCoerceStringToBoolean() throws JSONException { JSONArray array = new JSONArray(); array.put("maybe"); try { array.getBoolean(0); fail(); } catch (JSONException ignored) { } assertFalse(array.optBoolean(0)); assertTrue(array.optBoolean(0, true)); } @Test public void testNulls() throws JSONException { JSONArray array = new JSONArray(); array.put(3, null); array.put(0, JSONObject.NULL); assertEquals(4, array.length()); assertEquals("[null,null,null,null]", array.toString()); // there's 2 ways to represent null; each behaves differently! assertEquals(JSONObject.NULL, array.get(0)); try { array.get(1); fail(); } catch (JSONException ignored) { } try { array.get(2); fail(); } catch (JSONException ignored) { } try { array.get(3); fail(); } catch (JSONException ignored) { } assertEquals(JSONObject.NULL, array.opt(0)); assertNull(array.opt(1)); assertNull(array.opt(2)); assertNull(array.opt(3)); assertTrue(array.isNull(0)); assertTrue(array.isNull(1)); assertTrue(array.isNull(2)); assertTrue(array.isNull(3)); assertEquals("null", array.optString(0)); assertEquals("", array.optString(1)); assertEquals("", array.optString(2)); assertEquals("", array.optString(3)); } /** * Our behaviour is questioned by this bug: * http://code.google.com/p/android/issues/detail?id=7257 */ @Test public void testParseNullYieldsJSONObjectNull() throws JSONException { JSONArray array = new JSONArray("[\"null\",null]"); array.put(null); assertEquals("null", array.get(0)); assertEquals(JSONObject.NULL, array.get(1)); try { array.get(2); fail(); } catch (JSONException ignored) { } assertEquals("null", array.getString(0)); assertEquals("null", array.getString(1)); try { array.getString(2); fail(); } catch (JSONException ignored) { } } @Test public void testNumbers() throws JSONException { JSONArray array = new JSONArray(); array.put(Double.MIN_VALUE); array.put(9223372036854775806L); array.put(Double.MAX_VALUE); array.put(-0d); JSONObject objElement = new JSONObject(); array.put(objElement); JSONArray arrElement = new JSONArray(); array.put(arrElement); array.put(Integer.MIN_VALUE); assertEquals(7, array.length()); // toString() and getString(int) return different values for -0d assertEquals("[4.9E-324,9223372036854775806,1.7976931348623157E308,-0,{},[],-2147483648]", array.toString()); assertEquals(Double.MIN_VALUE, array.get(0)); assertEquals(9223372036854775806L, array.get(1)); assertEquals(Double.MAX_VALUE, array.get(2)); assertEquals(-0d, array.get(3)); assertEquals(Double.MIN_VALUE, array.getDouble(0), 0); assertEquals(9.223372036854776E18, array.getDouble(1), 0); assertEquals(Double.MAX_VALUE, array.getDouble(2), 0); assertEquals(-0d, array.getDouble(3), 0); assertEquals(0, array.getLong(0)); assertEquals(9223372036854775806L, array.getLong(1)); assertEquals(Long.MAX_VALUE, array.getLong(2)); assertEquals(0, array.getLong(3)); assertEquals(0, array.getInt(0)); assertEquals(-2, array.getInt(1)); assertEquals(Integer.MAX_VALUE, array.getInt(2)); assertEquals(0, array.getInt(3)); assertEquals(Double.MIN_VALUE, array.opt(0)); assertEquals(Double.MIN_VALUE, array.optDouble(0), 0); assertEquals(0, array.optLong(0, 1L)); assertEquals(0, array.optInt(0, 1)); assertEquals("4.9E-324", array.getString(0)); assertEquals("9223372036854775806", array.getString(1)); assertEquals("1.7976931348623157E308", array.getString(2)); assertEquals(objElement, array.getJSONObject(4)); assertEquals(arrElement, array.getJSONArray(5)); assertEquals(Integer.MIN_VALUE, array.getInt(6)); JSONArray other = new JSONArray(); other.put(Double.MIN_VALUE); other.put(9223372036854775806L); other.put(Double.MAX_VALUE); other.put(-0d); other.put(objElement); other.put(arrElement); other.put(Integer.MIN_VALUE); assertEquals(array, other); other.put(0, 0L); other.put(6, Integer.MIN_VALUE); assertNotEquals(array, other); } @Test public void testStrings() throws JSONException { JSONArray array = new JSONArray(); array.put("true"); array.put("5.5"); array.put("9223372036854775806"); array.put("null"); array.put("5\"8' tall"); assertEquals(5, array.length()); assertEquals("[\"true\",\"5.5\",\"9223372036854775806\",\"null\",\"5\\\"8' tall\"]", array.toString()); // although the documentation doesn't mention it, join() escapes text and wraps // strings in quotes assertEquals("\"true\" \"5.5\" \"9223372036854775806\" \"null\" \"5\\\"8' tall\"", array.join(" ")); assertEquals("true", array.get(0)); assertEquals("null", array.getString(3)); assertEquals("5\"8' tall", array.getString(4)); assertEquals("true", array.opt(0)); assertEquals("5.5", array.optString(1)); assertEquals("9223372036854775806", array.optString(2, null)); assertEquals("null", array.optString(3, "-1")); assertFalse(array.isNull(0)); assertFalse(array.isNull(3)); assertTrue(array.getBoolean(0)); assertTrue(array.optBoolean(0)); assertTrue(array.optBoolean(0, false)); assertEquals(0, array.optInt(0)); assertEquals(-2, array.optInt(0, -2)); assertEquals(5.5d, array.getDouble(1), 0); assertEquals(5L, array.getLong(1)); assertEquals(5, array.getInt(1)); assertEquals(5, array.optInt(1, 3)); // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a // double and then converting that to a long. This is consistent with JavaScript. assertEquals(9223372036854775807L, array.getLong(2)); assertEquals(9.223372036854776E18, array.getDouble(2), 0); assertEquals(Integer.MAX_VALUE, array.getInt(2)); assertFalse(array.isNull(3)); try { array.getDouble(3); fail(); } catch (JSONException e) { // expected } assertEquals(Double.NaN, array.optDouble(3), 0); assertEquals(-1.0d, array.optDouble(3, -1.0d), 0); } @Test public void testJoin() throws JSONException { JSONArray array = new JSONArray(); array.put(null); assertEquals("null", array.join(" & ")); array.put("\""); assertEquals("null & \"\\\"\"", array.join(" & ")); array.put(5); assertEquals("null & \"\\\"\" & 5", array.join(" & ")); array.put(true); assertEquals("null & \"\\\"\" & 5 & true", array.join(" & ")); array.put(new JSONArray(Arrays.asList(true, false))); assertEquals("null & \"\\\"\" & 5 & true & [true,false]", array.join(" & ")); array.put(new JSONObject(Collections.singletonMap("x", 6))); assertEquals("null & \"\\\"\" & 5 & true & [true,false] & {\"x\":6}", array.join(" & ")); } @Test public void testJoinWithNull() throws JSONException { JSONArray array = new JSONArray(Arrays.asList(5, 6)); assertEquals("5null6", array.join(null)); } @Test public void testJoinWithSpecialCharacters() throws JSONException { JSONArray array = new JSONArray(Arrays.asList(5, 6)); assertEquals("5\"6", array.join("\"")); } @Test public void testToJSONObject() throws JSONException { JSONArray keys = new JSONArray(); keys.put("a"); keys.put("b"); JSONArray values = new JSONArray(); values.put(5.5d); values.put(false); JSONObject object = values.toJSONObject(keys); assertEquals(5.5d, object.get("a")); assertEquals(false, object.get("b")); keys.put(0, "a"); values.put(0, 11.0d); assertEquals(5.5d, object.get("a")); } @Test public void testToJSONObjectWithNulls() throws JSONException { JSONArray keys = new JSONArray(); keys.put("a"); keys.put("b"); JSONArray values = new JSONArray(); values.put(5.5d); values.put(null); // null values are stripped! JSONObject object = values.toJSONObject(keys); assertEquals(1, object.length()); assertFalse(object.has("b")); assertEquals("{\"a\":5.5}", object.toString()); } @Test public void testToJSONObjectMoreNamesThanValues() throws JSONException { JSONArray keys = new JSONArray(); keys.put("a"); keys.put("b"); JSONArray values = new JSONArray(); values.put(5.5d); JSONObject object = values.toJSONObject(keys); assertEquals(1, object.length()); assertEquals(5.5d, object.get("a")); } @Test public void testToJSONObjectMoreValuesThanNames() throws JSONException { JSONArray keys = new JSONArray(); keys.put("a"); JSONArray values = new JSONArray(); values.put(5.5d); values.put(11.0d); JSONObject object = values.toJSONObject(keys); assertEquals(1, object.length()); assertEquals(5.5d, object.get("a")); } @Test public void testToJSONObjectNullKey() throws JSONException { JSONArray keys = new JSONArray(); keys.put(JSONObject.NULL); JSONArray values = new JSONArray(); values.put(5.5d); JSONObject object = values.toJSONObject(keys); assertEquals(1, object.length()); assertEquals(5.5d, object.get("null")); } @Test public void putCollection() { JSONArray array = new JSONArray(); array.put(Arrays.asList(1, 2, 3)); array.put(2, Arrays.asList(3, 2, 1)); assertEquals(3, array.length()); assertTrue(array.isNull(1)); JSONArray list0 = array.getJSONArray(0); assertEquals(3, list0.length()); assertEquals(1, list0.getInt(0)); assertEquals(2, list0.getInt(1)); assertEquals(3, list0.get(2)); JSONArray list2 = array.getJSONArray(2); assertEquals(3, list2.length()); assertEquals(3, list2.getInt(0)); assertEquals(2, list2.getInt(1)); assertEquals(1, list2.get(2)); } @Test public void putCollectionNull() { JSONArray array = new JSONArray(); Collection c = null; array.put(c); assertEquals(1, array.length()); assertTrue(array.isNull(0)); } @Test public void testPutUnsupportedNumbers() throws JSONException { JSONArray array = new JSONArray(); try { array.put(Double.NaN); fail(); } catch (JSONException e) { // expected } try { array.put(0, Double.NEGATIVE_INFINITY); fail(); } catch (JSONException e) { // expected } try { array.put(0, Double.POSITIVE_INFINITY); fail(); } catch (JSONException e) { // expected } } @Test public void testPutUnsupportedNumbersAsObject() throws JSONException { JSONArray array = new JSONArray(); array.put(Double.valueOf(Double.NaN)); array.put(Double.valueOf(Double.NEGATIVE_INFINITY)); array.put(Double.valueOf(Double.POSITIVE_INFINITY)); assertNull(array.toString()); } /** * Although JSONArray is usually defensive about which numbers it accepts, * it doesn't check inputs in its constructor. */ @Test public void testCreateWithUnsupportedNumbers() throws JSONException { JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN)); assertEquals(2, array.length()); assertEquals(5.5, array.getDouble(0), 0); assertEquals(Double.NaN, array.getDouble(1), 0); } @Test public void testToStringWithUnsupportedNumbers() throws JSONException { // when the array contains an unsupported number, toString returns null! JSONArray array = new JSONArray(Arrays.asList(5.5, Double.NaN)); assertNull(array.toString()); } @Test public void testToStringWithNulls() throws JSONException { // when the array contains an unsupported number, toString returns null! JSONArray array = new JSONArray(Arrays.asList( new JSONObject() .put("a", "A String") .put("n", 666) .put("null", null) .put("o", new JSONObject() .put("b", "B String") .put("null", null) .put("bool", false)) , new JSONObject() .put("a", "A String") .put("n", 666) .put("null", null) )); assertEquals("[{\"a\":\"A String\",\"n\":666,\"o\":{\"b\":\"B String\",\"bool\":false}},{\"a\":\"A String\",\"n\":666}]", array.toString()); } @Test public void testToString() { JSONObjectTest.MyPojo2 myPojo = new JSONObjectTest.MyPojo2(); String json = new JSONArray().put(myPojo).toString(); assertEquals("[{\"myProp3\":\"value3\",\"myProp4\":\"value4\",\"myProp5\":\"value5\"}]", json); } @Test public void testEmptyCollectionConstructor() throws JSONException { JSONArray array1 = new JSONArray(Collections.emptyList()); assertEquals(0, array1.length()); JSONArray array2 = new JSONArray((Collection) null); assertEquals(0, array2.length()); } @Test public void testListConstructorCopiesContents() throws JSONException { // have to use asList instead of Collections.singleton() to allow mutation //noinspection ArraysAsListWithZeroOrOneArgument List contents = Arrays.asList(5); JSONArray array = new JSONArray(contents); contents.set(0, 10); assertEquals(5, array.get(0)); } @Test(expected = NullPointerException.class) public void testObjectConstructorNull() throws JSONException { new JSONArray((Object) null); } @Test public void testTokenerConstructor() throws JSONException { JSONArray object = new JSONArray(new JSONTokener("[false]")); assertEquals(1, object.length()); assertEquals(false, object.get(0)); } @Test(expected = JSONException.class) public void testTokenerConstructorWrongType() throws JSONException { new JSONArray(new JSONTokener("{\"foo\": false}")); } @Test(expected = NullPointerException.class) public void testTokenerConstructorNull() throws JSONException { new JSONArray((JSONTokener) null); } @Test public void testTokenerConstructorParseFail() { try { new JSONArray(new JSONTokener("[")); fail(); } catch (JSONException ignored) { } catch (StackOverflowError e) { fail("Stack overflowed on input: \"[\""); } } @Test public void testStringConstructor() throws JSONException { JSONArray object = new JSONArray("[false]"); assertEquals(1, object.length()); assertEquals(false, object.get(0)); } @Test public void testStringConstructorWrongType() throws JSONException { try { new JSONArray("{\"foo\": false}"); fail(); } catch (JSONException ignored) { } try { new JSONArray(new Object()); fail(); } catch (JSONException ignored) { } } @Test(expected = NullPointerException.class) public void testStringConstructorNull() throws JSONException { new JSONArray((String) null); } @Test public void testStringConstructorParseFail() { try { new JSONArray("["); fail(); } catch (JSONException ignored) { } catch (StackOverflowError e) { fail("Stack overflowed on input: \"[\""); } } @Test public void testCreate() throws JSONException { JSONArray array = new JSONArray(Arrays.asList(5.5, true)); assertEquals(2, array.length()); assertEquals(5.5, array.getDouble(0), 0.0); assertEquals(true, array.get(1)); assertEquals("[5.5,true]", array.toString()); } @Test public void testAccessOutOfBounds() throws JSONException { JSONArray array = new JSONArray(); array.put("foo"); assertNull(array.opt(3)); assertNull(array.opt(-3)); assertEquals("", array.optString(3)); assertEquals("", array.optString(-3)); try { array.get(3); fail(); } catch (JSONException ignored) { } try { array.get(-3); fail(); } catch (JSONException ignored) { } try { array.getString(3); fail(); } catch (JSONException ignored) { } try { array.getString(-3); fail(); } catch (JSONException ignored) { } } @Test public void test_remove() { JSONArray a = new JSONArray(); assertNull(a.remove(-1)); assertNull(a.remove(0)); a.put("hello"); assertNull(a.remove(-1)); assertNull(a.remove(1)); assertEquals("hello", a.remove(0)); assertNull(a.remove(0)); } enum MyEnum {A, B, C} // https://code.google.com/p/android/issues/detail?id=62539 // but changed in open-json to return toString for all enums @Test public void testEnums() { // This works because it's in java.* and any class in there falls back to toString. JSONArray a1 = new JSONArray(java.lang.annotation.RetentionPolicy.values()); assertEquals("[\"SOURCE\",\"CLASS\",\"RUNTIME\"]", a1.toString()); // This doesn't because it's not. JSONArray a2 = new JSONArray(MyEnum.values()); assertEquals("[\"A\",\"B\",\"C\"]", a2.toString()); } @Test public void testIterator() { JSONArray a = new JSONArray(); a.put(1234); a.put("foo"); Iterator iterator = a.iterator(); assertEquals(1234, iterator.next()); assertEquals("foo", iterator.next()); assertFalse(iterator.hasNext()); } } openjson-1.0.12/src/test/java/com/github/openjson/JSONFunctionTestObject.java000066400000000000000000000004511363767403400271360ustar00rootroot00000000000000package com.github.openjson; /** * Class to test the function hack */ public class JSONFunctionTestObject { private String value; public JSONFunctionTestObject(String value) { this.value = value; } @Override public String toString() { return value; } }openjson-1.0.12/src/test/java/com/github/openjson/JSONObjectTest.java000066400000000000000000001341771363767403400254450ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.junit.Test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * This black box test was written without inspecting the non-free org.json sourcecode. */ @SuppressWarnings({"unused", "SpellCheckingInspection"}) public class JSONObjectTest { @Test public void testKeyset() { JSONObject x = new JSONObject("{'a':1, 'b':2, 'c':3}"); Set k = new TreeSet(Arrays.asList("a", "b", "c")); assertEquals(x.keySet(), k); x = new JSONObject("{}"); assertEquals(x.keySet().size(), 0); } @Test public void testEmptyObject() throws JSONException { JSONObject object = new JSONObject(); assertEquals(0, object.length()); // bogus (but documented) behaviour: returns null rather than the empty object! assertNull(object.names()); // returns null rather than an empty array! assertNull(object.toJSONArray(new JSONArray())); assertEquals("{}", object.toString()); assertEquals("{}", object.toString(5)); try { object.get("foo"); fail(); } catch (JSONException ignored) { } try { object.getBoolean("foo"); fail(); } catch (JSONException ignored) { } try { object.getDouble("foo"); fail(); } catch (JSONException ignored) { } try { object.getInt("foo"); fail(); } catch (JSONException ignored) { } try { object.getJSONArray("foo"); fail(); } catch (JSONException ignored) { } try { object.getJSONObject("foo"); fail(); } catch (JSONException ignored) { } try { object.getLong("foo"); fail(); } catch (JSONException ignored) { } try { object.getString("foo"); fail(); } catch (JSONException ignored) { } assertFalse(object.has("foo")); assertTrue(object.isNull("foo")); // isNull also means "is not present" assertNull(object.opt("foo")); assertFalse(object.optBoolean("foo")); assertTrue(object.optBoolean("foo", true)); assertEquals(Double.NaN, object.optDouble("foo"), 0); assertEquals(5.0, object.optDouble("foo", 5.0), 0); assertEquals(0, object.optInt("foo")); assertEquals(5, object.optInt("foo", 5)); assertNull(object.optJSONArray("foo")); assertNull(object.optJSONObject("foo")); assertEquals(0, object.optLong("foo")); assertEquals(Long.MAX_VALUE - 1, object.optLong("foo", Long.MAX_VALUE - 1)); assertEquals("", object.optString("foo")); // empty string is default! assertEquals("bar", object.optString("foo", "bar")); assertNull(object.remove("foo")); } @Test public void testEqualsAndHashCode() throws JSONException { JSONObject a = new JSONObject(); JSONObject b = new JSONObject(); // JSON object doesn't override either equals or hashCode (!) assertNotEquals(a, b); assertEquals(a.hashCode(), System.identityHashCode(a)); } @Test public void testGet() throws JSONException { JSONObject object = new JSONObject(); Object value = new Object(); object.put("foo", value); object.put("bar", new Object()); object.put("baz", new Object()); assertSame(value, object.get("foo")); try { object.get("FOO"); fail(); } catch (JSONException ignored) { } try { object.put(null, value); fail(); } catch (JSONException ignored) { } try { object.get(null); fail(); } catch (JSONException ignored) { } } @Test public void testPut() throws JSONException { JSONObject object = new JSONObject(); assertSame(object, object.put("foo", true)); object.put("foo", false); assertEquals(false, object.get("foo")); object.put("foo", 5.0d); assertEquals(5.0d, object.get("foo")); object.put("foo", 0); assertEquals(0, object.get("foo")); object.put("bar", Long.MAX_VALUE - 1); assertEquals(Long.MAX_VALUE - 1, object.get("bar")); object.put("baz", "x"); assertEquals("x", object.get("baz")); object.put("bar", JSONObject.NULL); assertSame(JSONObject.NULL, object.get("bar")); } @Test public void testPutNullRemoves() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "bar"); object.put("foo", null); assertEquals(0, object.length()); assertFalse(object.has("foo")); try { object.get("foo"); fail(); } catch (JSONException ignored) { } } @Test public void testPutOpt() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "bar"); object.putOpt("foo", null); assertEquals("bar", object.get("foo")); object.putOpt(null, null); assertEquals(1, object.length()); object.putOpt(null, "bar"); assertEquals(1, object.length()); } @Test public void testPutOptUnsupportedNumbers() throws JSONException { JSONObject object = new JSONObject(); try { object.putOpt("foo", Double.NaN); fail(); } catch (JSONException ignored) { } try { object.putOpt("foo", Double.NEGATIVE_INFINITY); fail(); } catch (JSONException ignored) { } try { object.putOpt("foo", Double.POSITIVE_INFINITY); fail(); } catch (JSONException ignored) { } } @Test public void testRemove() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "bar"); assertNull(object.remove(null)); assertNull(object.remove("")); assertNull(object.remove("bar")); assertEquals("bar", object.remove("foo")); assertNull(object.remove("foo")); } @Test public void testBooleans() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", true); object.put("bar", false); object.put("baz", "true"); object.put("quux", "false"); assertEquals(4, object.length()); assertTrue(object.getBoolean("foo")); assertFalse(object.getBoolean("bar")); assertTrue(object.getBoolean("baz")); assertFalse(object.getBoolean("quux")); assertFalse(object.isNull("foo")); assertFalse(object.isNull("quux")); assertTrue(object.has("foo")); assertTrue(object.has("quux")); assertFalse(object.has("missing")); assertTrue(object.optBoolean("foo")); assertFalse(object.optBoolean("bar")); assertTrue(object.optBoolean("baz")); assertFalse(object.optBoolean("quux")); assertFalse(object.optBoolean("missing")); assertTrue(object.optBoolean("foo", true)); assertFalse(object.optBoolean("bar", true)); assertTrue(object.optBoolean("baz", true)); assertFalse(object.optBoolean("quux", true)); assertTrue(object.optBoolean("missing", true)); object.put("foo", "truE"); object.put("bar", "FALSE"); assertTrue(object.getBoolean("foo")); assertFalse(object.getBoolean("bar")); assertTrue(object.optBoolean("foo")); assertFalse(object.optBoolean("bar")); assertTrue(object.optBoolean("foo", false)); assertFalse(object.optBoolean("bar", false)); } @Test public void testCoerceStringToNumber() throws JSONException { JSONObject object = new JSONObject(); object.put("v1", "1"); object.put("v2", "-1"); assertEquals(1, object.getInt("v1")); assertEquals(-1, object.getLong("v2")); } @Test public void testCoerceStringToNumberWithFail() throws JSONException { JSONObject object = new JSONObject(); object.put("key", "not a number"); try { object.getLong("key"); fail(); } catch (JSONException ignored) { } assertEquals(0, object.optLong("key")); } // http://code.google.com/p/android/issues/detail?id=16411 @Test public void testCoerceStringToBoolean() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "maybe"); try { object.getBoolean("foo"); fail(); } catch (JSONException ignored) { } assertFalse(object.optBoolean("foo")); assertTrue(object.optBoolean("foo", true)); } @Test public void testNumbers() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", Double.MIN_VALUE); object.put("bar", 9223372036854775806L); object.put("baz", Double.MAX_VALUE); object.put("quux", -0d); assertEquals(4, object.length()); String toString = object.toString(); assertTrue(toString, toString.contains("\"foo\":4.9E-324")); assertTrue(toString, toString.contains("\"bar\":9223372036854775806")); assertTrue(toString, toString.contains("\"baz\":1.7976931348623157E308")); // toString() and getString() return different values for -0d! assertTrue(toString, toString.contains("\"quux\":-0}") // no trailing decimal point || toString.contains("\"quux\":-0,")); assertEquals(Double.MIN_VALUE, object.get("foo")); assertEquals(9223372036854775806L, object.get("bar")); assertEquals(Double.MAX_VALUE, object.get("baz")); assertEquals(-0d, object.get("quux")); assertEquals(Double.MIN_VALUE, object.getDouble("foo"), 0); assertEquals(9.223372036854776E18, object.getDouble("bar"), 0); assertEquals(Double.MAX_VALUE, object.getDouble("baz"), 0); assertEquals(-0d, object.getDouble("quux"), 0); assertEquals(0, object.getLong("foo")); assertEquals(9223372036854775806L, object.getLong("bar")); assertEquals(Long.MAX_VALUE, object.getLong("baz")); assertEquals(0, object.getLong("quux")); assertEquals(0, object.getInt("foo")); assertEquals(-2, object.getInt("bar")); assertEquals(Integer.MAX_VALUE, object.getInt("baz")); assertEquals(0, object.getInt("quux")); assertEquals(Double.MIN_VALUE, object.opt("foo")); assertEquals(9223372036854775806L, object.optLong("bar")); assertEquals(Double.MAX_VALUE, object.optDouble("baz"), 0); assertEquals(0, object.optInt("quux")); assertEquals(Double.MIN_VALUE, object.opt("foo")); assertEquals(9223372036854775806L, object.optLong("bar")); assertEquals(Double.MAX_VALUE, object.optDouble("baz"), 0); assertEquals(0, object.optInt("quux")); assertEquals(Double.MIN_VALUE, object.optDouble("foo", 5.0d), 0); assertEquals(9223372036854775806L, object.optLong("bar", 1L)); assertEquals(Long.MAX_VALUE, object.optLong("baz", 1L)); assertEquals(0, object.optInt("quux", -1)); assertEquals("4.9E-324", object.getString("foo")); assertEquals("9223372036854775806", object.getString("bar")); assertEquals("1.7976931348623157E308", object.getString("baz")); assertEquals("-0.0", object.getString("quux")); } @Test public void testFloats() throws JSONException { JSONObject object = new JSONObject(); try { object.put("foo", (Float) Float.NaN); fail(); } catch (JSONException ignored) { } try { object.put("foo", (Float) Float.NEGATIVE_INFINITY); fail(); } catch (JSONException ignored) { } try { object.put("foo", (Float) Float.POSITIVE_INFINITY); fail(); } catch (JSONException ignored) { } } @Test public void testOtherNumbers() throws JSONException { Number nan = new Number() { @Override public int intValue() { throw new UnsupportedOperationException(); } @Override public long longValue() { throw new UnsupportedOperationException(); } @Override public float floatValue() { throw new UnsupportedOperationException(); } @Override public double doubleValue() { return Double.NaN; } @Override public String toString() { return "x"; } }; JSONObject object = new JSONObject(); try { object.put("foo", nan); fail("Object.put() accepted a NaN (via a custom Number class)"); } catch (JSONException ignored) { } } @Test public void testForeignObjects() throws JSONException { Object foreign = new Object() { @Override public String toString() { return "x"; } }; // foreign object types are accepted and treated as Strings! JSONObject object = new JSONObject(); object.put("foo", foreign); assertEquals("{\"foo\":\"x\"}", object.toString()); } @Test public void testNullKeys() { try { new JSONObject().put(null, false); fail(); } catch (JSONException ignored) { } try { new JSONObject().put(null, 0.0d); fail(); } catch (JSONException ignored) { } try { new JSONObject().put(null, 5); fail(); } catch (JSONException ignored) { } try { new JSONObject().put(null, 5L); fail(); } catch (JSONException ignored) { } try { new JSONObject().put(null, "foo"); fail(); } catch (JSONException ignored) { } } @Test public void testStrings() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "true"); object.put("bar", "5.5"); object.put("baz", "9223372036854775806"); object.put("quux", "null"); object.put("height", "5\"8' tall"); assertTrue(object.toString().contains("\"foo\":\"true\"")); assertTrue(object.toString().contains("\"bar\":\"5.5\"")); assertTrue(object.toString().contains("\"baz\":\"9223372036854775806\"")); assertTrue(object.toString().contains("\"quux\":\"null\"")); assertTrue(object.toString().contains("\"height\":\"5\\\"8' tall\"")); assertEquals("true", object.get("foo")); assertEquals("null", object.getString("quux")); assertEquals("5\"8' tall", object.getString("height")); assertEquals("true", object.opt("foo")); assertEquals("5.5", object.optString("bar")); assertEquals("true", object.optString("foo", "x")); assertFalse(object.isNull("foo")); assertTrue(object.getBoolean("foo")); assertTrue(object.optBoolean("foo")); assertTrue(object.optBoolean("foo", false)); assertEquals(0, object.optInt("foo")); assertEquals(-2, object.optInt("foo", -2)); assertEquals(5.5d, object.getDouble("bar"), 0); assertEquals(5L, object.getLong("bar")); assertEquals(5, object.getInt("bar")); assertEquals(5, object.optInt("bar", 3)); // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a // double and then converting that to a long. This is consistent with JavaScript. assertEquals(9223372036854775807L, object.getLong("baz")); assertEquals(9.223372036854776E18, object.getDouble("baz"), 0); assertEquals(Integer.MAX_VALUE, object.getInt("baz")); assertFalse(object.isNull("quux")); try { object.getDouble("quux"); fail(); } catch (JSONException e) { // expected } assertEquals(Double.NaN, object.optDouble("quux"), 0); assertEquals(-1.0d, object.optDouble("quux", -1.0d), 0); object.put("foo", "TRUE"); assertTrue(object.getBoolean("foo")); } @Test public void testJSONObjects() throws JSONException { JSONObject object = new JSONObject(); JSONArray a = new JSONArray(); JSONObject b = new JSONObject(); object.put("foo", a); object.put("bar", b); assertSame(a, object.getJSONArray("foo")); assertSame(b, object.getJSONObject("bar")); try { object.getJSONObject("foo"); fail(); } catch (JSONException ignored) { } try { object.getJSONArray("bar"); fail(); } catch (JSONException ignored) { } assertEquals(a, object.optJSONArray("foo")); assertEquals(b, object.optJSONObject("bar")); assertNull(object.optJSONArray("bar")); assertNull(object.optJSONObject("foo")); } @Test public void testNullCoercionToString() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", JSONObject.NULL); assertEquals("null", object.getString("foo")); } @Test public void testArrayCoercion() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "[true]"); try { object.getJSONArray("foo"); fail(); } catch (JSONException ignored) { } } @Test public void testObjectCoercion() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "{}"); try { object.getJSONObject("foo"); fail(); } catch (JSONException ignored) { } } @Test public void testPutCollection() throws JSONException { JSONObject object = new JSONObject(); object.put("array", Arrays.asList("1", 1, 3D)); JSONArray array = object.getJSONArray("array"); assertEquals(3, array.length()); assertEquals("1", array.get(0)); assertEquals(1, array.get(1)); assertEquals(3D, array.get(2)); } @SuppressWarnings("unchecked") @Test public void testPutMap() throws JSONException { JSONObject object = new JSONObject(); object.put("map", new HashMap() {{ put("x", "1"); put("y", 1); put("z", 3D); }}); JSONObject map = object.getJSONObject("map"); assertEquals(3, map.length()); assertEquals("1", map.get("x")); assertEquals(1, map.get("y")); assertEquals(3D, map.get("z")); } @Test public void testAccumulateValueChecking() throws JSONException { JSONObject object = new JSONObject(); try { object.accumulate("foo", Double.NaN); fail(); } catch (JSONException ignored) { } object.accumulate("foo", 1); try { object.accumulate("foo", Double.NaN); fail(); } catch (JSONException ignored) { } object.accumulate("foo", 2); try { object.accumulate("foo", Double.NaN); fail(); } catch (JSONException ignored) { } } @Test public void testToJSONArray() throws JSONException { JSONObject object = new JSONObject(); Object value = new Object(); object.put("foo", true); object.put("bar", 5.0d); object.put("baz", -0.0d); object.put("quux", value); JSONArray names = new JSONArray(); names.put("baz"); names.put("quux"); names.put("foo"); JSONArray array = object.toJSONArray(names); assertEquals(-0.0d, array.get(0)); assertEquals(value, array.get(1)); assertEquals(true, array.get(2)); object.put("foo", false); assertEquals(true, array.get(2)); } @Test public void testToJSONArrayMissingNames() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", true); object.put("bar", 5.0d); object.put("baz", JSONObject.NULL); JSONArray names = new JSONArray(); names.put("bar"); names.put("foo"); names.put("quux"); names.put("baz"); JSONArray array = object.toJSONArray(names); assertEquals(4, array.length()); assertEquals(5.0d, array.get(0)); assertEquals(true, array.get(1)); try { array.get(2); fail(); } catch (JSONException ignored) { } assertEquals(JSONObject.NULL, array.get(3)); } @Test public void testToJSONArrayNull() throws JSONException { JSONObject object = new JSONObject(); assertNull(object.toJSONArray(null)); object.put("foo", 5); try { object.toJSONArray(null); } catch (JSONException ignored) { } } @Test public void testToJSONArrayEndsUpEmpty() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); JSONArray array = new JSONArray(); array.put("bar"); assertEquals(1, object.toJSONArray(array).length()); } @Test public void testToJSONArrayNonString() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); object.put("null", 10); object.put("false", 15); JSONArray names = new JSONArray(); names.put(JSONObject.NULL); names.put(false); names.put("foo"); // array elements are converted to strings to do name lookups on the map! JSONArray array = object.toJSONArray(names); assertEquals(3, array.length()); assertEquals(10, array.get(0)); assertEquals(15, array.get(1)); assertEquals(5, array.get(2)); } @Test public void testPutUnsupportedNumbers() throws JSONException { JSONObject object = new JSONObject(); try { object.put("foo", Double.NaN); fail(); } catch (JSONException ignored) { } try { object.put("foo", Double.NEGATIVE_INFINITY); fail(); } catch (JSONException ignored) { } try { object.put("foo", Double.POSITIVE_INFINITY); fail(); } catch (JSONException ignored) { } } @Test public void testPutUnsupportedNumbersAsObjects() throws JSONException { JSONObject object = new JSONObject(); try { object.put("foo", (Double) Double.NaN); fail(); } catch (JSONException ignored) { } try { object.put("foo", (Double) Double.NEGATIVE_INFINITY); fail(); } catch (JSONException ignored) { } try { object.put("foo", (Double) Double.POSITIVE_INFINITY); fail(); } catch (JSONException ignored) { } } /** * Although JSONObject is usually defensive about which numbers it accepts, * it doesn't check inputs in its constructor. */ @Test public void testCreateWithUnsupportedNumbers() throws JSONException { Map contents = new HashMap(); contents.put("foo", Double.NaN); contents.put("bar", Double.NEGATIVE_INFINITY); contents.put("baz", Double.POSITIVE_INFINITY); JSONObject object = new JSONObject(contents); assertEquals(Double.NaN, object.get("foo")); assertEquals(Double.NEGATIVE_INFINITY, object.get("bar")); assertEquals(Double.POSITIVE_INFINITY, object.get("baz")); } @Test public void testToStringWithUnsupportedNumbers() { // when the object contains an unsupported number, toString returns null! JSONObject object = new JSONObject(Collections.singletonMap("foo", Double.NaN)); assertNull(object.toString()); } @Test public void testMapConstructorWithNull() throws JSONException { JSONObject object = new JSONObject((Map)null); assertEquals(0, object.keySet().size()); } @Test public void testMapConstructorCopiesContents() throws JSONException { Map contents = new HashMap(); contents.put("foo", 5); JSONObject object = new JSONObject(contents); contents.put("foo", 10); assertEquals(5, object.get("foo")); } @Test public void testMapConstructorWithBogusEntries() { Map contents = new HashMap(); contents.put(5, 5); try { new JSONObject(contents); fail("JSONObject constructor doesn't validate its input!"); } catch (Exception ignored) { } } /** * Warning: org.json package does allow nulls as values. We don't. * See {@link JSONObject#JSONObject(Map)} for details. */ @Test(expected = NullPointerException.class) public void testMapConstructorWithBogusEntries2() { Map contents = new HashMap(); contents.put(null, 5); new JSONObject(contents); } @Test public void testTokenerConstructor() throws JSONException { JSONObject object = new JSONObject(new JSONTokener("{\"foo\": false}")); assertEquals(1, object.length()); assertEquals(false, object.get("foo")); } @Test public void testTokenerConstructorWrongType() throws JSONException { try { new JSONObject(new JSONTokener("[\"foo\", false]")); fail(); } catch (JSONException ignored) { } } @Test public void testTokenerConstructorNull() throws JSONException { try { new JSONObject((JSONTokener) null); fail(); } catch (NullPointerException ignored) { } } @Test public void testTokenerConstructorParseFail() { try { new JSONObject(new JSONTokener("{")); fail(); } catch (JSONException ignored) { } } @Test public void testStringConstructor() throws JSONException { JSONObject object = new JSONObject("{\"foo\": false}"); assertEquals(1, object.length()); assertEquals(false, object.get("foo")); } @Test public void testStringConstructorWrongType() throws JSONException { try { new JSONObject("[\"foo\", false]"); fail(); } catch (JSONException ignored) { } } @Test public void testStringConstructorNull() throws JSONException { try { new JSONObject((String) null); fail(); } catch (NullPointerException ignored) { } } @Test public void testStringConstructorParseFail() { try { new JSONObject("{"); fail(); } catch (JSONException ignored) { } } @Test public void testCopyConstructor() throws JSONException { JSONObject source = new JSONObject(); source.put("a", JSONObject.NULL); source.put("b", false); source.put("c", 5); JSONObject copy = new JSONObject(source, new String[]{"a", "c"}); assertEquals(2, copy.length()); assertEquals(JSONObject.NULL, copy.get("a")); assertEquals(5, copy.get("c")); assertNull(copy.opt("b")); } @Test public void testCopyConstructorMissingName() throws JSONException { JSONObject source = new JSONObject(); source.put("a", JSONObject.NULL); source.put("b", false); source.put("c", 5); JSONObject copy = new JSONObject(source, new String[]{"a", "c", "d"}); assertEquals(2, copy.length()); assertEquals(JSONObject.NULL, copy.get("a")); assertEquals(5, copy.get("c")); assertEquals(0, copy.optInt("b")); } @Test public void testCopyConstructorNoNames() throws JSONException { JSONObject obj = new JSONObject().put("var", "Test 1"); JSONObject source = new JSONObject(); source.put("a", JSONObject.NULL); source.put("b", false); source.put("c", 5); source.put("comp", obj); JSONObject copy = new JSONObject(source); assertEquals(source.length(), copy.length()); assertEquals(source.get("a"), copy.get("a")); assertEquals(source.get("b"), copy.opt("b")); assertEquals(source.get("c"), copy.get("c")); assertEquals(source.get("comp"), copy.get("comp")); } @Test public void testArrayConstructor() throws JSONException { final JSONArray array = new JSONArray(new Object[] { 1, 2, 3 }); JSONObject obj = new JSONObject(array); assertEquals("The result should be empty object", "{}", obj.toString()); } @Test public void testAccumulateMutatesInPlace() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); object.accumulate("foo", 6); JSONArray array = object.getJSONArray("foo"); assertEquals("[5,6]", array.toString()); object.accumulate("foo", 7); assertEquals("[5,6,7]", array.toString()); } @Test public void testAccumulateExistingArray() throws JSONException { JSONArray array = new JSONArray(); JSONObject object = new JSONObject(); object.put("foo", array); object.accumulate("foo", 5); assertEquals("[5]", array.toString()); } @Test public void testAccumulatePutArray() throws JSONException { JSONObject object = new JSONObject(); object.accumulate("foo", 5); assertEquals("{\"foo\":5}", object.toString()); object.accumulate("foo", new JSONArray()); assertEquals("{\"foo\":[5,[]]}", object.toString()); } @Test public void testAccumulateNull() { JSONObject object = new JSONObject(); try { object.accumulate(null, 5); fail(); } catch (JSONException ignored) { } } @Test public void testEmptyStringKey() throws JSONException { JSONObject object = new JSONObject(); object.put("", 5); assertEquals(5, object.get("")); assertEquals("{\"\":5}", object.toString()); } @Test public void testNullValue() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", JSONObject.NULL); object.put("bar", null); // there are two ways to represent null; each behaves differently! assertTrue(object.has("foo")); assertFalse(object.has("bar")); assertTrue(object.isNull("foo")); assertTrue(object.isNull("bar")); } @Test public void testNullValue_equalsAndHashCode() { assertTrue(JSONObject.NULL.equals(null)); // guaranteed by javadoc // not guaranteed by javadoc, but seems like a good idea assertEquals(0, JSONObject.NULL.hashCode()); } @Test public void testHas() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); assertTrue(object.has("foo")); assertFalse(object.has("bar")); assertFalse(object.has(null)); } @Test public void testOptNull() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", "bar"); assertNull(object.opt(null)); assertFalse(object.optBoolean(null)); assertEquals(Double.NaN, object.optDouble(null), 0); assertEquals(0, object.optInt(null)); assertEquals(0L, object.optLong(null)); assertNull(object.optJSONArray(null)); assertNull(object.optJSONObject(null)); assertEquals("", object.optString(null)); assertTrue(object.optBoolean(null, true)); assertEquals(0.0d, object.optDouble(null, 0.0d), 0); assertEquals(1, object.optInt(null, 1)); assertEquals(1L, object.optLong(null, 1L)); assertEquals("baz", object.optString(null, "baz")); } @Test public void testToStringWithIndentFactor() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", new JSONArray(Arrays.asList(5, 6))); object.put("bar", new JSONObject()); String foobar = "{\n" + " \"foo\": [\n" + " 5,\n" + " 6\n" + " ],\n" + " \"bar\": {}\n" + "}"; String barfoo = "{\n" + " \"bar\": {},\n" + " \"foo\": [\n" + " 5,\n" + " 6\n" + " ]\n" + "}"; String string = object.toString(5); assertTrue(string, foobar.equals(string) || barfoo.equals(string)); } @Test public void testToStringWithNulls() throws JSONException { JSONObject obj = new JSONObject(); obj.put("a", "A String") .put("n", 666) .put("null", null) .put("o", new JSONObject() .put("b", "B String") .put("null", null) .put("bool", false)); assertEquals("{\"a\":\"A String\",\"n\":666,\"o\":{\"b\":\"B String\",\"bool\":false}}", obj.toString()); } @Test public void testToStringWithWrappedNulls() throws JSONException { JSONObject obj = new JSONObject(new Bean(100, "Test String", null)); assertEquals("{\"i\":100,\"s\":\"Test String\"}", obj.toString()); } /** * This test shows that toString from openjson is not compatible with org.json for null collections. * org.json prints empty array when openjson strips null regardless of a type. */ @Test @SuppressWarnings("RedundantCast") public void testToStringWithNullCollection() throws JSONException { JSONObject obj = new JSONObject(); obj.put("c", ((Collection)null)); assertEquals("{}", obj.toString()); // org.json will output {"c":[]} } /** * This test shows that toString from openjson is not compatible with org.json for null collections. * org.json prints empty array when openjson strips null regardless of a type. */ @Test @SuppressWarnings("RedundantCast") public void testToStringWithNullMap() throws JSONException { JSONObject obj = new JSONObject(); obj.put("m", ((Map)null)); assertEquals("{}", obj.toString()); // org.json will output {"m":{}} } @Test public void testNames() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); object.put("bar", 6); object.put("baz", 7); JSONArray array = object.names(); assertTrue(array.toString().contains("foo")); assertTrue(array.toString().contains("bar")); assertTrue(array.toString().contains("baz")); } @Test public void testKeysEmptyObject() { JSONObject object = new JSONObject(); assertFalse(object.keys().hasNext()); try { object.keys().next(); fail(); } catch (NoSuchElementException ignored) { } } @Test public void testKeys() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); object.put("bar", 6); object.put("foo", 7); Iterator keys = object.keys(); Set result = new HashSet(); assertTrue(keys.hasNext()); result.add(keys.next()); assertTrue(keys.hasNext()); result.add(keys.next()); assertFalse(keys.hasNext()); assertEquals(new HashSet(Arrays.asList("foo", "bar")), result); try { keys.next(); fail(); } catch (NoSuchElementException ignored) { } } @Test public void testMutatingKeysMutatesObject() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); Iterator keys = object.keys(); keys.next(); keys.remove(); assertEquals(0, object.length()); } @Test public void testQuote() { // covered by JSONStringerTest.testEscaping } @Test public void testQuoteNull() throws JSONException { assertEquals("\"\"", JSONObject.quote(null)); } @Test public void testNumberToString() throws JSONException { assertEquals("5", JSONObject.numberToString(5)); assertEquals("-0", JSONObject.numberToString(-0.0d)); assertEquals("9223372036854775806", JSONObject.numberToString(9223372036854775806L)); assertEquals("4.9E-324", JSONObject.numberToString(Double.MIN_VALUE)); assertEquals("1.7976931348623157E308", JSONObject.numberToString(Double.MAX_VALUE)); try { JSONObject.numberToString(Double.NaN); fail(); } catch (JSONException ignored) { } try { JSONObject.numberToString(Double.NEGATIVE_INFINITY); fail(); } catch (JSONException ignored) { } try { JSONObject.numberToString(Double.POSITIVE_INFINITY); fail(); } catch (JSONException ignored) { } assertEquals("0.001", JSONObject.numberToString(new BigDecimal("0.001"))); assertEquals("9223372036854775806", JSONObject.numberToString(new BigInteger("9223372036854775806"))); try { JSONObject.numberToString(null); fail(); } catch (JSONException ignored) { } } @Test public void test_wrap() { assertEquals(JSONObject.NULL, JSONObject.wrap(null)); JSONArray a = new JSONArray(); assertEquals(a, JSONObject.wrap(a)); JSONObject o = new JSONObject(); assertEquals(o, JSONObject.wrap(o)); assertEquals(JSONObject.NULL, JSONObject.wrap(JSONObject.NULL)); assertTrue(JSONObject.wrap(new byte[0]) instanceof JSONArray); assertTrue(JSONObject.wrap(new ArrayList()) instanceof JSONArray); assertTrue(JSONObject.wrap(new HashMap()) instanceof JSONObject); assertTrue(JSONObject.wrap(new Object()) instanceof String); assertTrue(JSONObject.wrap(new Date()) instanceof String); assertTrue(JSONObject.wrap(new Bar()) instanceof JSONObject); assertTrue(JSONObject.wrap(true) instanceof Boolean); assertTrue(JSONObject.wrap((byte) 1) instanceof Byte); assertTrue(JSONObject.wrap('\0') instanceof Character); assertTrue(JSONObject.wrap(0.0D) instanceof Double); assertTrue(JSONObject.wrap(0.0F) instanceof Float); assertTrue(JSONObject.wrap(0) instanceof Integer); assertTrue(JSONObject.wrap(0L) instanceof Long); assertTrue(JSONObject.wrap((short) 0) instanceof Short); assertTrue(JSONObject.wrap("hello") instanceof String); assertNull(JSONObject.wrap( new Object() { public String getX() { throw new IllegalStateException("unsupported"); } })); } // https://code.google.com/p/android/issues/detail?id=55114 @Test public void test_toString_listAsMapValue() { ArrayList list = new ArrayList(); list.add("a"); list.add(new ArrayList()); Map map = new TreeMap(); map.put("x", "l"); map.put("y", list); assertEquals("{\"x\":\"l\",\"y\":[\"a\",[]]}", new JSONObject(map).toString()); } @Test public void testAppendExistingInvalidKey() throws JSONException { JSONObject object = new JSONObject(); object.put("foo", 5); try { object.append("foo", 6); fail(); } catch (JSONException ignored) { } } @Test public void testAppendExistingArray() throws JSONException { JSONArray array = new JSONArray(); JSONObject object = new JSONObject(); object.put("foo", array); object.append("foo", 5); assertEquals("[5]", array.toString()); } @Test public void testAppendPutArray() throws JSONException { JSONObject object = new JSONObject(); object.append("foo", 5); assertEquals("{\"foo\":[5]}", object.toString()); object.append("foo", new JSONArray()); assertEquals("{\"foo\":[5,[]]}", object.toString()); } @Test public void testAppendNull() { JSONObject object = new JSONObject(); try { object.append(null, 5); fail(); } catch (JSONException ignored) { } } // https://code.google.com/p/android/issues/detail?id=103641 @Test public void testInvalidUnicodeEscape() { try { new JSONObject("{\"q\":\"\\u\", \"r\":[]}"); fail(); } catch (JSONException ignored) { } } @Test public void testBeanThings() { Foo f = new Foo(); assertEquals("{\"a\":1,\"b\":1,\"c\":\"c\",\"d\":[{\"e\":\"echo\"}]}", new JSONObject(f).toString()); } @Test(expected = JSONException.class) public void testBeanThingsWithErrors() { Foo f = new Foo() { @Override public double getA() { throw new IllegalStateException("Error!"); } }; new JSONObject(f); } @Test public void testGetNames() { assertArrayEquals(new String[]{"a", "b", "c", "d"}, JSONObject.getNames(new JSONObject(new Foo()))); } private static class Foo { public double getA() { return 1.0; } public int getB() { return 1; } public String getC() { return "c"; } public List getD() { ArrayList r = new ArrayList(); r.add(new Bar()); return r; } } private static class Bar { public String getE() { return "echo"; } } private static class Bean { private Integer i; private String s; private Object o; Bean() {} Bean(Integer i, String s, Object o) { this.i = i; this.s = s; this.o = o; } public Integer getI() { return i; } public void setI(Integer i) { this.i = i; } public String getS() { return s; } public void setS(String s) { this.s = s; } public Object getO() { return o; } public void setO(Object o) { this.o = o; } } @Test public void testEnumWrapper() { Object y = JSONObject.wrap(E.A); assertEquals("A", y); assertTrue(y instanceof String); } enum E { A { @Override int key() { return 1; } }, B { @Override int key() { return 2; } }; int key() { return -1; } } @Test public void testToString() { MyPojo1 myPojo1 = new MyPojo1(); MyPojo2 myPojo2 = new MyPojo2(); String json1 = new JSONObject(myPojo1).toString(); String json2 = new JSONObject().put("myPojo2", myPojo2).toString(); assertEquals("{\"myProp1\":\"value1\",\"myProp2\":\"value2\"}", json1); assertEquals("{\"myPojo2\":{\"myProp3\":\"value3\",\"myProp4\":\"value4\",\"myProp5\":\"value5\"}}", json2); } public static class MyPojo1 { private String myProp1 = "value1"; private String myProp2 = "value2"; public MyPojo1() { } public String getMyProp1() { return this.myProp1; } public String getMyProp2() { return this.myProp2; } } public static class MyPojo2 implements JSONString { private String myProp3 = "value3"; private String myProp4 = "value4"; public MyPojo2() { } public String getMyProp3() { return this.myProp3; } public String getMyProp4() { return this.myProp4; } @Override public String toJSONString() { JSONObject object = new JSONObject(JSONObject.objectAsMap(this)); object.put("myProp5", "value5"); return object.toString(); } } @Test public void testArrayJSONString() { List myArray = Arrays.asList(new MyPojo1(), new MyPojo2()); String expected = "[{\"myProp1\":\"value1\",\"myProp2\":\"value2\"},{\"myProp3\":\"value3\",\"myProp4\":\"value4\",\"myProp5\":\"value5\"}]"; String json = new JSONArray(myArray).toString(); assertEquals(expected, json); } } openjson-1.0.12/src/test/java/com/github/openjson/JSONStringerTest.java000066400000000000000000000344321363767403400260250ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.util.concurrent.TimeUnit; import org.junit.Test; /** * This black box test was written without inspecting the non-free org.json sourcecode. */ public class JSONStringerTest { @Test public void testJSONFunctionHackTest() { JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("key"); stringer.value(new JSONFunctionTestObject("window.test('foo' + \"bar\")")); stringer.endObject(); assertEquals("{\"key\":window.test('foo' + \"bar\")}", stringer.toString()); } @Test public void testEmptyStringer() { // why isn't this the empty string? assertNull(new JSONStringer().toString()); } @Test public void testValueJSONNull() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.value(JSONObject.NULL); stringer.endArray(); assertEquals("[null]", stringer.toString()); } @Test public void testEmptyObject() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.endObject(); assertEquals("{}", stringer.toString()); } @Test public void testEmptyArray() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.endArray(); assertEquals("[]", stringer.toString()); } @Test public void testArray() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.value(false); stringer.value(5.0); stringer.value(5L); stringer.value("five"); stringer.value(null); stringer.endArray(); assertEquals("[false,5,5,\"five\",null]", stringer.toString()); } @Test public void testValueObjectMethods() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.value(Boolean.FALSE); stringer.value(Double.valueOf(5.0)); stringer.value(Long.valueOf(5L)); stringer.endArray(); assertEquals("[false,5,5]", stringer.toString()); } @Test public void testKeyValue() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("a").value(false); stringer.key("b").value(5.0); stringer.key("c").value(5L); stringer.key("d").value("five"); stringer.key("e").value(null); stringer.endObject(); assertEquals("{\"a\":false," + "\"b\":5," + "\"c\":5," + "\"d\":\"five\"," + "\"e\":null}", stringer.toString()); } @Test public void testCustomNameStringer() throws JSONException { JSONStringer stringer = new JSONStringer() { @Override protected JSONStringer createKey(String name) { out.append(name); return this; } }; stringer.object(); stringer.key("a").value(false); stringer.key("b").value(5.0); stringer.key("c").value(5L); stringer.key("d").value("five"); stringer.key("e").value(null); stringer.endObject(); assertEquals("{a:false," + "b:5," + "c:5," + "d:\"five\"," + "e:null}", stringer.toString()); } /** * Test what happens when extreme values are emitted. Such values are likely * to be rounded during parsing. */ @Test public void testNumericRepresentations() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.value(Long.MAX_VALUE); stringer.value(Double.MIN_VALUE); stringer.endArray(); assertEquals("[9223372036854775807,4.9E-324]", stringer.toString()); } @Test public void testWeirdNumbers() throws JSONException { try { new JSONStringer().array().value(Double.NaN); fail(); } catch (JSONException ignored) { } try { new JSONStringer().array().value(Double.NEGATIVE_INFINITY); fail(); } catch (JSONException ignored) { } try { new JSONStringer().array().value(Double.POSITIVE_INFINITY); fail(); } catch (JSONException ignored) { } JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.value(-0.0d); stringer.value(0.0d); stringer.endArray(); assertEquals("[-0,0]", stringer.toString()); } @Test public void testMismatchedScopes() { try { new JSONStringer().key("a"); fail(); } catch (JSONException ignored) { } try { new JSONStringer().value("a"); fail(); } catch (JSONException ignored) { } try { new JSONStringer().endObject(); fail(); } catch (JSONException ignored) { } try { new JSONStringer().endArray(); fail(); } catch (JSONException ignored) { } try { new JSONStringer().array().endObject(); fail(); } catch (JSONException ignored) { } try { new JSONStringer().object().endArray(); fail(); } catch (JSONException ignored) { } try { new JSONStringer().object().key("a").key("a"); fail(); } catch (JSONException ignored) { } try { new JSONStringer().object().value(false); fail(); } catch (JSONException ignored) { } } @Test public void testNullKey() { try { new JSONStringer().object().key(null); fail(); } catch (JSONException ignored) { } } @Test public void testRepeatedKey() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("a").value(true); stringer.key("a").value(false); stringer.endObject(); // JSONStringer doesn't attempt to detect duplicates assertEquals("{\"a\":true,\"a\":false}", stringer.toString()); } @Test public void testEmptyKey() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("").value(false); stringer.endObject(); assertEquals("{\"\":false}", stringer.toString()); // legit behaviour! } @Test public void testEscaping() throws JSONException { assertEscapedAllWays("a", "a"); assertEscapedAllWays("a\\\"", "a\""); assertEscapedAllWays("\\\"", "\""); assertEscapedAllWays(":", ":"); assertEscapedAllWays(",", ","); assertEscapedAllWays("\\b", "\b"); assertEscapedAllWays("\\f", "\f"); assertEscapedAllWays("\\n", "\n"); assertEscapedAllWays("\\r", "\r"); assertEscapedAllWays("\\t", "\t"); assertEscapedAllWays(" ", " "); assertEscapedAllWays("\\\\", "\\"); assertEscapedAllWays("{", "{"); assertEscapedAllWays("}", "}"); assertEscapedAllWays("[", "["); assertEscapedAllWays("]", "]"); assertEscapedAllWays("\\u0000", "\0"); assertEscapedAllWays("\\u0019", "\u0019"); assertEscapedAllWays(" ", "\u0020"); assertEscapedAllWays("<\\/foo>", ""); } private static void assertEscapedAllWays(String escaped, String original) throws JSONException { assertEquals("{\"" + escaped + "\":false}", new JSONStringer().object().key(original).value(false).endObject().toString()); assertEquals("{\"a\":\"" + escaped + "\"}", new JSONStringer().object().key("a").value(original).endObject().toString()); assertEquals("[\"" + escaped + "\"]", new JSONStringer().array().value(original).endArray().toString()); assertEquals("\"" + escaped + "\"", JSONObject.quote(original)); } @Test public void testJSONArrayAsValue() throws JSONException { JSONArray array = new JSONArray(); array.put(false); JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.value(array); stringer.endArray(); assertEquals("[[false]]", stringer.toString()); } @Test public void testJSONObjectAsValue() throws JSONException { JSONObject object = new JSONObject(); object.put("a", false); JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("b").value(object); stringer.endObject(); assertEquals("{\"b\":{\"a\":false}}", stringer.toString()); } @Test public void testArrayNestingMaxDepthSupports20() throws JSONException { JSONStringer stringer = new JSONStringer(); for (int i = 0; i < 20; i++) { stringer.array(); } for (int i = 0; i < 20; i++) { stringer.endArray(); } assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringer.toString()); stringer = new JSONStringer(); for (int i = 0; i < 20; i++) { stringer.array(); } } @Test public void testObjectNestingMaxDepthSupports20() throws JSONException { JSONStringer stringer = new JSONStringer(); for (int i = 0; i < 20; i++) { stringer.object(); stringer.key("a"); } stringer.value(false); for (int i = 0; i < 20; i++) { stringer.endObject(); } assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":" + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":false" + "}}}}}}}}}}}}}}}}}}}}", stringer.toString()); stringer = new JSONStringer(); for (int i = 0; i < 20; i++) { stringer.object(); stringer.key("a"); } } @Test public void testMixedMaxDepthSupports20() throws JSONException { JSONStringer stringer = new JSONStringer(); for (int i = 0; i < 20; i += 2) { stringer.array(); stringer.object(); stringer.key("a"); } stringer.value(false); for (int i = 0; i < 20; i += 2) { stringer.endObject(); stringer.endArray(); } assertEquals("[{\"a\":[{\"a\":[{\"a\":[{\"a\":[{\"a\":" + "[{\"a\":[{\"a\":[{\"a\":[{\"a\":[{\"a\":false" + "}]}]}]}]}]}]}]}]}]}]", stringer.toString()); stringer = new JSONStringer(); for (int i = 0; i < 20; i += 2) { stringer.array(); stringer.object(); stringer.key("a"); } } @Test public void testMaxDepthWithArrayValue() throws JSONException { JSONArray array = new JSONArray(); array.put(false); JSONStringer stringer = new JSONStringer(); for (int i = 0; i < 20; i++) { stringer.array(); } stringer.value(array); for (int i = 0; i < 20; i++) { stringer.endArray(); } assertEquals("[[[[[[[[[[[[[[[[[[[[[false]]]]]]]]]]]]]]]]]]]]]", stringer.toString()); } @Test public void testMaxDepthWithObjectValue() throws JSONException { JSONObject object = new JSONObject(); object.put("a", false); JSONStringer stringer = new JSONStringer(); for (int i = 0; i < 20; i++) { stringer.object(); stringer.key("b"); } stringer.value(object); for (int i = 0; i < 20; i++) { stringer.endObject(); } assertEquals("{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":" + "{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":{\"b\":" + "{\"a\":false}}}}}}}}}}}}}}}}}}}}}", stringer.toString()); } @Test public void testMultipleRoots() throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.array(); stringer.endArray(); try { stringer.object(); fail(); } catch (JSONException ignored) { } } @Test public void testEnums() { JSONObject x = new JSONObject(); x.put("a", TimeUnit.SECONDS); x.put("b", "xyx"); JSONStringer s = new JSONStringer(); s.array(); s.value(x); s.endArray(); assertEquals("[{\"a\":\"SECONDS\",\"b\":\"xyx\"}]", s.toString()); } @Test public void testJsonString() { JSONObject x = new JSONObject(); x.put("a", new Goo()); JSONStringer s = new JSONStringer(); s.array(); s.value(x); s.endArray(); // note lack of quotes assertEquals("[{\"a\":fffooo}]", s.toString()); } @Test public void testIndent() { JSONObject o = new JSONObject() .put("b", new JSONObject().put("a", false)); String result = "{\"b\":{\"a\":false}}"; assertEquals(result, o.toString(new JSONStringer(-1))); assertEquals(result, o.toString(new JSONStringer())); assertEquals("{\n \"b\": {\n \"a\": false\n }\n}", o.toString(new JSONStringer(3))); } private static class Goo implements JSONString { @Override public String toJSONString() { return "fffooo"; } } } openjson-1.0.12/src/test/java/com/github/openjson/JSONTokenerTest.java000066400000000000000000000556601363767403400256450ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import junit.framework.AssertionFailedError; import junit.framework.TestCase; /** * This black box test was written without inspecting the non-free org.json sourcecode. */ public class JSONTokenerTest extends TestCase { public void testNulls() throws JSONException { // JSONTokener accepts null, only to fail later on almost all APIs! new JSONTokener((String) null).back(); try { new JSONTokener((String) null).more(); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).next(); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).next(3); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).next('A'); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).nextClean(); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).nextString('"'); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).nextTo('A'); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).nextTo("ABC"); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).nextValue(); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).skipPast("ABC"); fail(); } catch (NullPointerException ignored) { } try { new JSONTokener((String) null).skipTo('A'); fail(); } catch (NullPointerException ignored) { } assertEquals("foo! at character 0 of null", new JSONTokener((String) null).syntaxError("foo!").getMessage()); assertEquals(" at character 0 of null", new JSONTokener((String) null).toString()); } public void testEmptyString() throws JSONException { JSONTokener backTokener = new JSONTokener(""); backTokener.back(); assertEquals(" at character 0 of ", backTokener.toString()); assertFalse(new JSONTokener("").more()); assertEquals('\0', new JSONTokener("").next()); try { new JSONTokener("").next(3); fail(); } catch (JSONException ignored) { } try { new JSONTokener("").next('A'); fail(); } catch (JSONException ignored) { } assertEquals('\0', new JSONTokener("").nextClean()); try { new JSONTokener("").nextString('"'); fail(); } catch (JSONException ignored) { } assertEquals("", new JSONTokener("").nextTo('A')); assertEquals("", new JSONTokener("").nextTo("ABC")); try { new JSONTokener("").nextValue(); fail(); } catch (JSONException ignored) { } new JSONTokener("").skipPast("ABC"); assertEquals('\0', new JSONTokener("").skipTo('A')); assertEquals("foo! at character 0 of ", new JSONTokener("").syntaxError("foo!").getMessage()); assertEquals(" at character 0 of ", new JSONTokener("").toString()); } public void testCharacterNavigation() throws JSONException { JSONTokener abcdeTokener = new JSONTokener("ABCDE"); assertEquals('A', abcdeTokener.next()); assertEquals('B', abcdeTokener.next('B')); assertEquals("CD", abcdeTokener.next(2)); try { abcdeTokener.next(2); fail(); } catch (JSONException ignored) { } assertEquals('E', abcdeTokener.nextClean()); assertEquals('\0', abcdeTokener.next()); assertFalse(abcdeTokener.more()); abcdeTokener.back(); assertTrue(abcdeTokener.more()); assertEquals('E', abcdeTokener.next()); } public void testBackNextAndMore() throws JSONException { JSONTokener abcTokener = new JSONTokener("ABC"); assertTrue(abcTokener.more()); abcTokener.next(); abcTokener.next(); assertTrue(abcTokener.more()); abcTokener.next(); assertFalse(abcTokener.more()); abcTokener.back(); assertTrue(abcTokener.more()); abcTokener.next(); assertFalse(abcTokener.more()); abcTokener.back(); abcTokener.back(); abcTokener.back(); abcTokener.back(); // you can back up before the beginning of a String! assertEquals('A', abcTokener.next()); } public void testNextMatching() throws JSONException { JSONTokener abcdTokener = new JSONTokener("ABCD"); assertEquals('A', abcdTokener.next('A')); try { abcdTokener.next('C'); // although it failed, this op consumes a character of input fail(); } catch (JSONException ignored) { } assertEquals('C', abcdTokener.next('C')); assertEquals('D', abcdTokener.next('D')); try { abcdTokener.next('E'); fail(); } catch (JSONException ignored) { } } public void testNextN() throws JSONException { JSONTokener abcdeTokener = new JSONTokener("ABCDEF"); assertEquals("", abcdeTokener.next(0)); try { abcdeTokener.next(7); fail(); } catch (JSONException ignored) { } assertEquals("ABC", abcdeTokener.next(3)); try { abcdeTokener.next(4); fail(); } catch (JSONException ignored) { } } public void testNextNWithAllRemaining() throws JSONException { JSONTokener tokener = new JSONTokener("ABCDEF"); tokener.next(3); try { tokener.next(3); } catch (JSONException e) { AssertionFailedError error = new AssertionFailedError("off-by-one error?"); error.initCause(e); throw error; } } public void testNext0() throws JSONException { JSONTokener tokener = new JSONTokener("ABCDEF"); tokener.next(5); tokener.next(); try { tokener.next(0); } catch (JSONException e) { Error error = new AssertionFailedError("Returning an empty string should be valid"); error.initCause(e); throw error; } } public void testNextCleanComments() throws JSONException { JSONTokener tokener = new JSONTokener( " A /*XX*/B/*XX//XX\n//XX\nXX*/C//X//X//X\nD/*X*///X\n"); assertEquals('A', tokener.nextClean()); assertEquals('B', tokener.nextClean()); assertEquals('C', tokener.nextClean()); assertEquals('D', tokener.nextClean()); assertEquals('\0', tokener.nextClean()); } public void testNextCleanNestedCStyleComments() throws JSONException { JSONTokener tokener = new JSONTokener("A /* B /* C */ D */ E"); assertEquals('A', tokener.nextClean()); assertEquals('D', tokener.nextClean()); assertEquals('*', tokener.nextClean()); assertEquals('/', tokener.nextClean()); assertEquals('E', tokener.nextClean()); } /** * Some applications rely on parsing '#' to lead an end-of-line comment. * http://b/2571423 */ public void testNextCleanHashComments() throws JSONException { JSONTokener tokener = new JSONTokener("A # B */ /* C */ \nD #"); assertEquals('A', tokener.nextClean()); assertEquals('D', tokener.nextClean()); assertEquals('\0', tokener.nextClean()); } public void testNextCleanCommentsTrailingSingleSlash() throws JSONException { JSONTokener tokener = new JSONTokener(" / S /"); assertEquals('/', tokener.nextClean()); assertEquals('S', tokener.nextClean()); assertEquals('/', tokener.nextClean()); assertEquals("nextClean doesn't consume a trailing slash", '\0', tokener.nextClean()); } public void testNextCleanTrailingOpenComment() throws JSONException { try { new JSONTokener(" /* ").nextClean(); fail(); } catch (JSONException ignored) { } assertEquals('\0', new JSONTokener(" // ").nextClean()); } public void testNextCleanNewlineDelimiters() throws JSONException { assertEquals('B', new JSONTokener(" // \r\n B ").nextClean()); assertEquals('B', new JSONTokener(" // \n B ").nextClean()); assertEquals('B', new JSONTokener(" // \r B ").nextClean()); } public void testNextCleanSkippedWhitespace() throws JSONException { assertEquals("character tabulation", 'A', new JSONTokener("\tA").nextClean()); assertEquals("line feed", 'A', new JSONTokener("\nA").nextClean()); assertEquals("carriage return", 'A', new JSONTokener("\rA").nextClean()); assertEquals("space", 'A', new JSONTokener(" A").nextClean()); } /** * Tests which characters tokener treats as ignorable whitespace. See Kevin Bourrillion's * list * of whitespace characters. */ public void testNextCleanRetainedWhitespace() throws JSONException { assertNotClean("null", '\u0000'); assertNotClean("next line", '\u0085'); assertNotClean("non-breaking space", '\u00a0'); assertNotClean("ogham space mark", '\u1680'); assertNotClean("mongolian vowel separator", '\u180e'); assertNotClean("en quad", '\u2000'); assertNotClean("em quad", '\u2001'); assertNotClean("en space", '\u2002'); assertNotClean("em space", '\u2003'); assertNotClean("three-per-em space", '\u2004'); assertNotClean("four-per-em space", '\u2005'); assertNotClean("six-per-em space", '\u2006'); assertNotClean("figure space", '\u2007'); assertNotClean("punctuation space", '\u2008'); assertNotClean("thin space", '\u2009'); assertNotClean("hair space", '\u200a'); assertNotClean("zero-width space", '\u200b'); assertNotClean("left-to-right mark", '\u200e'); assertNotClean("right-to-left mark", '\u200f'); assertNotClean("line separator", '\u2028'); assertNotClean("paragraph separator", '\u2029'); assertNotClean("narrow non-breaking space", '\u202f'); assertNotClean("medium mathematical space", '\u205f'); assertNotClean("ideographic space", '\u3000'); assertNotClean("line tabulation", '\u000b'); assertNotClean("form feed", '\u000c'); assertNotClean("information separator 4", '\u001c'); assertNotClean("information separator 3", '\u001d'); assertNotClean("information separator 2", '\u001e'); assertNotClean("information separator 1", '\u001f'); } private void assertNotClean(String name, char c) throws JSONException { assertEquals("The character " + name + " is not whitespace according to the JSON spec.", c, new JSONTokener(new String(new char[] { c, 'A' })).nextClean()); } public void testNextString() throws JSONException { assertEquals("", new JSONTokener("'").nextString('\'')); assertEquals("", new JSONTokener("\"").nextString('\"')); assertEquals("ABC", new JSONTokener("ABC'DEF").nextString('\'')); assertEquals("ABC", new JSONTokener("ABC'''DEF").nextString('\'')); // nextString permits slash-escaping of arbitrary characters! assertEquals("ABC", new JSONTokener("A\\B\\C'DEF").nextString('\'')); JSONTokener tokener = new JSONTokener(" 'abc' 'def' \"ghi\""); tokener.next(); assertEquals('\'', tokener.next()); assertEquals("abc", tokener.nextString('\'')); tokener.next(); assertEquals('\'', tokener.next()); assertEquals("def", tokener.nextString('\'')); tokener.next(); assertEquals('"', tokener.next()); assertEquals("ghi", tokener.nextString('\"')); assertFalse(tokener.more()); } public void testNextStringNoDelimiter() throws JSONException { try { new JSONTokener("").nextString('\''); fail(); } catch (JSONException ignored) { } JSONTokener tokener = new JSONTokener(" 'abc"); tokener.next(); tokener.next(); try { tokener.next('\''); fail(); } catch (JSONException ignored) { } } public void testNextStringEscapedQuote() throws JSONException { try { new JSONTokener("abc\\").nextString('"'); fail(); } catch (JSONException ignored) { } // we're mixing Java escaping like \" and JavaScript escaping like \\\" // which makes these tests extra tricky to read! assertEquals("abc\"def", new JSONTokener("abc\\\"def\"ghi").nextString('"')); assertEquals("abc\\def", new JSONTokener("abc\\\\def\"ghi").nextString('"')); assertEquals("abc/def", new JSONTokener("abc\\/def\"ghi").nextString('"')); assertEquals("abc\bdef", new JSONTokener("abc\\bdef\"ghi").nextString('"')); assertEquals("abc\fdef", new JSONTokener("abc\\fdef\"ghi").nextString('"')); assertEquals("abc\ndef", new JSONTokener("abc\\ndef\"ghi").nextString('"')); assertEquals("abc\rdef", new JSONTokener("abc\\rdef\"ghi").nextString('"')); assertEquals("abc\tdef", new JSONTokener("abc\\tdef\"ghi").nextString('"')); } public void testNextStringUnicodeEscaped() throws JSONException { // we're mixing Java escaping like \\ and JavaScript escaping like \\u assertEquals("abc def", new JSONTokener("abc\\u0020def\"ghi").nextString('"')); assertEquals("abcU0020def", new JSONTokener("abc\\U0020def\"ghi").nextString('"')); // JSON requires 4 hex characters after a unicode escape try { new JSONTokener("abc\\u002\"").nextString('"'); fail(); } catch (JSONException ignored) { } try { new JSONTokener("abc\\u").nextString('"'); fail(); } catch (JSONException ignored) { } try { new JSONTokener("abc\\u \"").nextString('"'); fail(); } catch (JSONException ignored) { } assertEquals("abc\"def", new JSONTokener("abc\\u0022def\"ghi").nextString('"')); try { new JSONTokener("abc\\u000G\"").nextString('"'); fail(); } catch (JSONException ignored) { } } public void testNextStringNonQuote() throws JSONException { assertEquals("AB", new JSONTokener("ABC").nextString('C')); assertEquals("ABCD", new JSONTokener("AB\\CDC").nextString('C')); assertEquals("AB\nC", new JSONTokener("AB\\nCn").nextString('n')); } public void testNextTo() throws JSONException { assertEquals("ABC", new JSONTokener("ABCDEFG").nextTo("DHI")); assertEquals("ABCDEF", new JSONTokener("ABCDEF").nextTo("")); JSONTokener tokener = new JSONTokener("ABC\rDEF\nGHI\r\nJKL"); assertEquals("ABC", tokener.nextTo("M")); assertEquals('\r', tokener.next()); assertEquals("DEF", tokener.nextTo("M")); assertEquals('\n', tokener.next()); assertEquals("GHI", tokener.nextTo("M")); assertEquals('\r', tokener.next()); assertEquals('\n', tokener.next()); assertEquals("JKL", tokener.nextTo("M")); tokener = new JSONTokener("ABCDEFGHI"); assertEquals("ABC", tokener.nextTo("DEF")); assertEquals("", tokener.nextTo("DEF")); assertEquals('D', tokener.next()); assertEquals("", tokener.nextTo("DEF")); assertEquals('E', tokener.next()); assertEquals("", tokener.nextTo("DEF")); assertEquals('F', tokener.next()); assertEquals("GHI", tokener.nextTo("DEF")); assertEquals("", tokener.nextTo("DEF")); tokener = new JSONTokener(" \t \fABC \t DEF"); assertEquals("ABC", tokener.nextTo("DEF")); assertEquals('D', tokener.next()); tokener = new JSONTokener(" \t \fABC \n DEF"); assertEquals("ABC", tokener.nextTo("\n")); assertEquals("", tokener.nextTo("\n")); tokener = new JSONTokener(""); try { tokener.nextTo(null); fail(); } catch (NullPointerException ignored) { } } public void testNextToTrimming() { assertEquals("ABC", new JSONTokener("\t ABC \tDEF").nextTo("DE")); assertEquals("ABC", new JSONTokener("\t ABC \tDEF").nextTo('D')); } public void testNextToTrailing() { assertEquals("ABC DEF", new JSONTokener("\t ABC DEF \t").nextTo("G")); assertEquals("ABC DEF", new JSONTokener("\t ABC DEF \t").nextTo('G')); } public void testNextToDoesntStopOnNull() { String message = "nextTo() shouldn't stop after \\0 characters"; JSONTokener tokener = new JSONTokener(" \0\t \fABC \n DEF"); assertEquals(message, "ABC", tokener.nextTo("D")); assertEquals(message, '\n', tokener.next()); assertEquals(message, "", tokener.nextTo("D")); } public void testNextToConsumesNull() { String message = "nextTo shouldn't consume \\0."; JSONTokener tokener = new JSONTokener("ABC\0DEF"); assertEquals(message, "ABC", tokener.nextTo("\0")); assertEquals(message, '\0', tokener.next()); assertEquals(message, "DEF", tokener.nextTo("\0")); } public void testSkipPast() { JSONTokener tokener = new JSONTokener("ABCDEF"); tokener.skipPast("ABC"); assertEquals('D', tokener.next()); tokener.skipPast("EF"); assertEquals('\0', tokener.next()); tokener = new JSONTokener("ABCDEF"); tokener.skipPast("ABCDEF"); assertEquals('\0', tokener.next()); tokener = new JSONTokener("ABCDEF"); tokener.skipPast("G"); assertEquals('\0', tokener.next()); tokener = new JSONTokener("ABC\0ABC"); tokener.skipPast("ABC"); assertEquals('\0', tokener.next()); assertEquals('A', tokener.next()); tokener = new JSONTokener("\0ABC"); tokener.skipPast("ABC"); assertEquals('\0', tokener.next()); tokener = new JSONTokener("ABC\nDEF"); tokener.skipPast("DEF"); assertEquals('\0', tokener.next()); tokener = new JSONTokener("ABC"); tokener.skipPast("ABCDEF"); assertEquals('\0', tokener.next()); tokener = new JSONTokener("ABCDABCDABCD"); tokener.skipPast("ABC"); assertEquals('D', tokener.next()); tokener.skipPast("ABC"); assertEquals('D', tokener.next()); tokener.skipPast("ABC"); assertEquals('D', tokener.next()); tokener = new JSONTokener(""); try { tokener.skipPast(null); fail(); } catch (NullPointerException ignored) { } } public void testSkipTo() { JSONTokener tokener = new JSONTokener("ABCDEF"); tokener.skipTo('A'); assertEquals('A', tokener.next()); tokener.skipTo('D'); assertEquals('D', tokener.next()); tokener.skipTo('G'); assertEquals('E', tokener.next()); tokener.skipTo('A'); assertEquals('F', tokener.next()); tokener = new JSONTokener("ABC\nDEF"); tokener.skipTo('F'); assertEquals('F', tokener.next()); tokener = new JSONTokener("ABCfDEF"); tokener.skipTo('F'); assertEquals('F', tokener.next()); tokener = new JSONTokener("ABC/* DEF */"); tokener.skipTo('D'); assertEquals('D', tokener.next()); } public void testSkipToStopsOnNull() { JSONTokener tokener = new JSONTokener("ABC\0DEF"); tokener.skipTo('F'); assertEquals("skipTo shouldn't stop when it sees '\\0'", 'F', tokener.next()); } public void testBomIgnoredAsFirstCharacterOfDocument() throws JSONException { JSONTokener tokener = new JSONTokener("\ufeff[]"); JSONArray array = (JSONArray) tokener.nextValue(); assertEquals(0, array.length()); } public void testBomTreatedAsCharacterInRestOfDocument() throws JSONException { JSONTokener tokener = new JSONTokener("[\ufeff]"); JSONArray array = (JSONArray) tokener.nextValue(); assertEquals(1, array.length()); } public void testDehexchar() { assertEquals( 0, JSONTokener.dehexchar('0')); assertEquals( 1, JSONTokener.dehexchar('1')); assertEquals( 2, JSONTokener.dehexchar('2')); assertEquals( 3, JSONTokener.dehexchar('3')); assertEquals( 4, JSONTokener.dehexchar('4')); assertEquals( 5, JSONTokener.dehexchar('5')); assertEquals( 6, JSONTokener.dehexchar('6')); assertEquals( 7, JSONTokener.dehexchar('7')); assertEquals( 8, JSONTokener.dehexchar('8')); assertEquals( 9, JSONTokener.dehexchar('9')); assertEquals(10, JSONTokener.dehexchar('A')); assertEquals(11, JSONTokener.dehexchar('B')); assertEquals(12, JSONTokener.dehexchar('C')); assertEquals(13, JSONTokener.dehexchar('D')); assertEquals(14, JSONTokener.dehexchar('E')); assertEquals(15, JSONTokener.dehexchar('F')); assertEquals(10, JSONTokener.dehexchar('a')); assertEquals(11, JSONTokener.dehexchar('b')); assertEquals(12, JSONTokener.dehexchar('c')); assertEquals(13, JSONTokener.dehexchar('d')); assertEquals(14, JSONTokener.dehexchar('e')); assertEquals(15, JSONTokener.dehexchar('f')); for (int c = 0; c <= 0xFFFF; c++) { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { continue; } assertEquals("dehexchar " + c, -1, JSONTokener.dehexchar((char) c)); } } } openjson-1.0.12/src/test/java/com/github/openjson/ParsingTest.java000066400000000000000000000254231363767403400251410ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import org.junit.Test; import java.util.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class ParsingTest { @Test public void testParsingNoObjects() { try { new JSONTokener("").nextValue(); fail(); } catch (JSONException ignored) { } } @Test public void testParsingLiterals() throws JSONException { assertParsed(Boolean.TRUE, "true"); assertParsed(Boolean.FALSE, "false"); assertParsed(JSONObject.NULL, "null"); assertParsed(JSONObject.NULL, "NULL"); assertParsed(Boolean.FALSE, "False"); assertParsed(Boolean.TRUE, "truE"); } @Test public void testParsingQuotedStrings() throws JSONException { assertParsed("abc", "\"abc\""); assertParsed("123", "\"123\""); assertParsed("foo\nbar", "\"foo\\nbar\""); assertParsed("foo bar", "\"foo\\u0020bar\""); assertParsed("\"{}[]/\\:,=;#", "\"\\\"{}[]/\\\\:,=;#\""); } @Test public void testParsingSingleQuotedStrings() throws JSONException { assertParsed("abc", "'abc'"); assertParsed("123", "'123'"); assertParsed("foo\nbar", "'foo\\nbar'"); assertParsed("foo bar", "'foo\\u0020bar'"); assertParsed("\"{}[]/\\:,=;#", "'\\\"{}[]/\\\\:,=;#'"); } @Test public void testParsingUnquotedStrings() throws JSONException { assertParsed("abc", "abc"); assertParsed("123abc", "123abc"); assertParsed("123e0x", "123e0x"); assertParsed("123e", "123e"); assertParsed("123ee21", "123ee21"); assertParsed("0xFFFFFFFFFFFFFFFFF", "0xFFFFFFFFFFFFFFFFF"); } /** * Unfortunately the original implementation attempts to figure out what * Java number type best suits an input value. */ @Test public void testParsingNumbersThatAreBestRepresentedAsLongs() throws JSONException { assertParsed(9223372036854775807L, "9223372036854775807"); assertParsed(9223372036854775806L, "9223372036854775806"); assertParsed(-9223372036854775808L, "-9223372036854775808"); assertParsed(-9223372036854775807L, "-9223372036854775807"); } @Test public void testParsingNumbersThatAreBestRepresentedAsIntegers() throws JSONException { assertParsed(0, "0"); assertParsed(5, "5"); assertParsed(-2147483648, "-2147483648"); assertParsed(2147483647, "2147483647"); } @Test public void testParsingNegativeZero() throws JSONException { assertParsed(0, "-0"); } @Test public void testParsingIntegersWithAdditionalPrecisionYieldDoubles() throws JSONException { assertParsed(1d, "1.00"); assertParsed(1d, "1.0"); assertParsed(0d, "0.0"); assertParsed(-0d, "-0.0"); } @Test public void testParsingNumbersThatAreBestRepresentedAsDoubles() throws JSONException { assertParsed(9.223372036854776E18, "9223372036854775808"); assertParsed(-9.223372036854776E18, "-9223372036854775809"); assertParsed(1.7976931348623157E308, "1.7976931348623157e308"); assertParsed(2.2250738585072014E-308, "2.2250738585072014E-308"); assertParsed(4.9E-324, "4.9E-324"); assertParsed(4.9E-324, "4.9e-324"); } @Test public void testParsingOctalNumbers() throws JSONException { assertParsed(5, "05"); assertParsed(8, "010"); assertParsed(1046, "02026"); } @Test public void testParsingHexNumbers() throws JSONException { assertParsed(5, "0x5"); assertParsed(16, "0x10"); assertParsed(17, "0X11"); assertParsed(8230, "0x2026"); assertParsed(180150010, "0xABCDEFA"); assertParsed(2077093803, "0x7BCDEFAB"); } @Test public void testParsingLargeHexValues() throws JSONException { assertParsed(Integer.MAX_VALUE, "0x7FFFFFFF"); String message = "Hex values are parsed as Strings if their signed " + "value is greater than Integer.MAX_VALUE."; assertParsed(message, 0x80000000L, "0x80000000"); } @Test public void test64BitHexValues() throws JSONException { // note that this is different from the same test in the original Android // this is due to the fact that Long.parseLong doesn't correctly handle // the value -1 expressed as unsigned hex if you use the normal JDK. Presumably // the Android equivalent program does this better. assertParsed("Large hex longs shouldn't yield ints or strings", 0xFFFFFFFFFFFFFFFL, "0xFFFFFFFFFFFFFFF"); } @Test public void testParsingWithCommentsAndWhitespace() throws JSONException { assertParsed("baz", " // foo bar \n baz"); assertParsed("baz", " // foo bar \r baz"); assertParsed("baz", " // foo bar \r\n baz"); assertParsed("baz", " # foo bar \n baz"); assertParsed("baz", " # foo bar \r baz"); assertParsed("baz", " # foo bar \r\n baz"); assertParsed(5, " /* foo bar \n baz */ 5"); assertParsed(5, " /* foo bar \n baz */ 5 // quux"); assertParsed(5, " 5 "); assertParsed(5, " 5 \r\n\t "); assertParsed(5, "\r\n\t 5 "); } @Test public void testParsingArrays() throws JSONException { assertParsed(array(), "[]"); assertParsed(array(5, 6, true), "[5,6,true]"); assertParsed(array(5, 6, array()), "[5,6,[]]"); assertParsed(array(5, 6, 7), "[5;6;7]"); assertParsed(array(5, 6, 7), "[5 , 6 \t; \r\n 7\n]"); assertParsed(array(5, 6, 7, null), "[5,6,7,]"); assertParsed(array(null, null), "[,]"); assertParsed(array(5, null, null, null, 5), "[5,,,,5]"); assertParsed(array(null, 5), "[,5]"); assertParsed(array(null, null, null), "[,,]"); assertParsed(array(null, null, null, 5), "[,,,5]"); } @Test public void testParsingObjects() throws JSONException { assertParsed(object("foo", 5), "{\"foo\": 5}"); assertParsed(object("foo", 5), "{foo: 5}"); assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\": 5, \"bar\": \"baz\"}"); assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\": 5; \"bar\": \"baz\"}"); assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\"= 5; \"bar\"= \"baz\"}"); assertParsed(object("foo", 5, "bar", "baz"), "{\"foo\"=> 5; \"bar\"=> \"baz\"}"); assertParsed(object("foo", object(), "bar", array()), "{\"foo\"=> {}; \"bar\"=> []}"); assertParsed(object("foo", object("foo", array(5, 6))), "{\"foo\": {\"foo\": [5, 6]}}"); assertParsed(object("foo", object("foo", array(5, 6))), "{\"foo\":\n\t{\t \"foo\":[5,\r6]}}"); } @Test public void testSyntaxProblemUnterminatedObject() { assertParseFail("{"); assertParseFail("{\"foo\""); assertParseFail("{\"foo\":"); assertParseFail("{\"foo\":bar"); assertParseFail("{\"foo\":bar,"); assertParseFail("{\"foo\":bar,\"baz\""); assertParseFail("{\"foo\":bar,\"baz\":"); assertParseFail("{\"foo\":bar,\"baz\":true"); assertParseFail("{\"foo\":bar,\"baz\":true,"); } @Test public void testSyntaxProblemEmptyString() { assertParseFail(""); } @Test public void testSyntaxProblemUnterminatedArray() { assertParseFail("["); assertParseFail("[,"); assertParseFail("[,,"); assertParseFail("[true"); assertParseFail("[true,"); assertParseFail("[true,,"); } @Test public void testSyntaxProblemMalformedObject() { assertParseFail("{:}"); assertParseFail("{\"key\":}"); assertParseFail("{:true}"); assertParseFail("{\"key\":true:}"); assertParseFail("{null:true}"); assertParseFail("{true:true}"); assertParseFail("{0xFF:true}"); } private void assertParseFail(String malformedJson) { try { new JSONTokener(malformedJson).nextValue(); fail("Successfully parsed: \"" + malformedJson + "\""); } catch (JSONException ignored) { } catch (StackOverflowError e) { fail("Stack overflowed on input: \"" + malformedJson + "\""); } } private JSONArray array(Object... elements) { return new JSONArray(Arrays.asList(elements)); } private JSONObject object(Object... keyValuePairs) throws JSONException { JSONObject result = new JSONObject(); for (int i = 0; i < keyValuePairs.length; i += 2) { result.put((String) keyValuePairs[i], keyValuePairs[i + 1]); } return result; } private void assertParsed(String message, Object expected, String json) throws JSONException { Object actual = new JSONTokener(json).nextValue(); actual = canonicalize(actual); expected = canonicalize(expected); assertEquals("For input \"" + json + "\" " + message, expected, actual); } private void assertParsed(Object expected, String json) throws JSONException { assertParsed("", expected, json); } /** * Since they don't implement equals or hashCode properly, this recursively * replaces JSONObjects with an equivalent HashMap, and JSONArrays with the * equivalent ArrayList. */ private Object canonicalize(Object input) throws JSONException { if (input instanceof JSONArray) { JSONArray array = (JSONArray) input; List result = new ArrayList(); for (int i = 0; i < array.length(); i++) { result.add(canonicalize(array.opt(i))); } return result; } else if (input instanceof JSONObject) { JSONObject object = (JSONObject) input; Map result = new HashMap(); for (Iterator i = object.keys(); i.hasNext(); ) { String key = (String) i.next(); result.put(key, canonicalize(object.get(key))); } return result; } else if (input == null || input.equals(JSONObject.NULL)) { return JSONObject.NULL; } else { return input; } } } openjson-1.0.12/src/test/java/com/github/openjson/SelfUseTest.java000066400000000000000000000205701363767403400251020ustar00rootroot00000000000000/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.openjson; import org.junit.Test; import static org.junit.Assert.assertEquals; /** * These tests checks self use calls. For the most part we doesn't attempt to * cover self-use, except in those cases where our clean room implementation * does it. *

*

This black box test was written without inspecting the non-free org.json * sourcecode. */ public class SelfUseTest { private int objectPutCalls = 0; private int objectGetCalls = 0; private int objectOptCalls = 0; private int objectOptTypeCalls = 0; private int arrayPutCalls = 0; private int arrayGetCalls = 0; private int arrayOptCalls = 0; private int arrayOptTypeCalls = 0; private int tokenerNextCalls = 0; private int tokenerNextValueCalls = 0; private final JSONObject object = new JSONObject() { @Override public JSONObject put(String name, Object value) throws JSONException { objectPutCalls++; return super.put(name, value); } @Override public Object get(String name) throws JSONException { objectGetCalls++; return super.get(name); } @Override public Object opt(String name) { objectOptCalls++; return super.opt(name); } @Override public boolean optBoolean(String key, boolean defaultValue) { objectOptTypeCalls++; return super.optBoolean(key, defaultValue); } @Override public double optDouble(String key, double defaultValue) { objectOptTypeCalls++; return super.optDouble(key, defaultValue); } @Override public int optInt(String key, int defaultValue) { objectOptTypeCalls++; return super.optInt(key, defaultValue); } @Override public long optLong(String key, long defaultValue) { objectOptTypeCalls++; return super.optLong(key, defaultValue); } @Override public String optString(String key, String defaultValue) { objectOptTypeCalls++; return super.optString(key, defaultValue); } }; private final JSONArray array = new JSONArray() { @Override public JSONArray put(int index, Object value) throws JSONException { arrayPutCalls++; return super.put(index, value); } @Override public Object get(int index) throws JSONException { arrayGetCalls++; return super.get(index); } @Override public Object opt(int index) { arrayOptCalls++; return super.opt(index); } @Override public boolean optBoolean(int index, boolean fallback) { arrayOptTypeCalls++; return super.optBoolean(index, fallback); } @Override public double optDouble(int index, double fallback) { arrayOptTypeCalls++; return super.optDouble(index, fallback); } @Override public long optLong(int index, long fallback) { arrayOptTypeCalls++; return super.optLong(index, fallback); } @Override public String optString(int index, String fallback) { arrayOptTypeCalls++; return super.optString(index, fallback); } @Override public int optInt(int index, int fallback) { arrayOptTypeCalls++; return super.optInt(index, fallback); } @Override public JSONArray optJSONArray(int index) { arrayOptTypeCalls++; return super.optJSONArray(index); } @Override public JSONObject optJSONObject(int index) { arrayOptTypeCalls++; return super.optJSONObject(index); } }; private final JSONTokener tokener = new JSONTokener("{\"foo\": [true]}") { @Override public char next() { tokenerNextCalls++; return super.next(); } @Override public Object nextValue() throws JSONException { tokenerNextValueCalls++; return super.nextValue(); } }; @Test public void testObjectPut() throws JSONException { object.putOpt("foo", "bar"); assertEquals(1, objectPutCalls); } @Test public void testObjectAccumulate() throws JSONException { object.accumulate("foo", "bar"); assertEquals(1, objectPutCalls); } @Test public void testObjectGetBoolean() throws JSONException { object.put("foo", "true"); object.getBoolean("foo"); assertEquals(1, objectGetCalls); } @Test public void testObjectOptType() throws JSONException { object.optBoolean("foo"); assertEquals(1, objectOptCalls); assertEquals(1, objectOptTypeCalls); object.optDouble("foo"); assertEquals(2, objectOptCalls); assertEquals(2, objectOptTypeCalls); object.optInt("foo"); assertEquals(3, objectOptCalls); assertEquals(3, objectOptTypeCalls); object.optLong("foo"); assertEquals(4, objectOptCalls); assertEquals(4, objectOptTypeCalls); object.optString("foo"); assertEquals(5, objectOptCalls); assertEquals(5, objectOptTypeCalls); } @Test public void testToJSONArray() throws JSONException { object.put("foo", 5); object.put("bar", 10); array.put("foo"); array.put("baz"); array.put("bar"); object.toJSONArray(array); assertEquals(3, arrayOptCalls); assertEquals(0, arrayOptTypeCalls); assertEquals(3, objectOptCalls); assertEquals(0, objectOptTypeCalls); } @Test public void testPutAtIndex() throws JSONException { array.put(10, false); assertEquals(1, arrayPutCalls); } @Test public void testIsNull() { array.isNull(5); assertEquals(1, arrayOptCalls); } @Test public void testArrayGetType() throws JSONException { array.put(true); array.getBoolean(0); assertEquals(1, arrayGetCalls); } @Test public void testArrayOptType() throws JSONException { array.optBoolean(3); assertEquals(1, arrayOptCalls); assertEquals(1, arrayOptTypeCalls); array.optDouble(3); assertEquals(2, arrayOptCalls); assertEquals(2, arrayOptTypeCalls); array.optInt(3); assertEquals(3, arrayOptCalls); assertEquals(3, arrayOptTypeCalls); array.optLong(3); assertEquals(4, arrayOptCalls); assertEquals(4, arrayOptTypeCalls); array.optString(3); assertEquals(5, arrayOptCalls); assertEquals(5, arrayOptTypeCalls); array.optJSONObject(3); assertEquals(6, arrayOptCalls); assertEquals(6, arrayOptTypeCalls); array.optJSONArray(3); assertEquals(7, arrayOptCalls); assertEquals(7, arrayOptTypeCalls); } @Test public void testToJSONObject() throws JSONException { array.put("foo"); array.put("baz"); array.put("bar"); JSONArray values = new JSONArray(); values.put(5.5d); values.put(11d); values.put(30); values.toJSONObject(array); assertEquals(3, arrayOptCalls); assertEquals(0, arrayOptTypeCalls); } @Test public void testNextExpecting() throws JSONException { tokener.next('{'); assertEquals(1, tokenerNextCalls); tokener.next('\"'); assertEquals(2, tokenerNextCalls); } @Test public void testNextValue() throws JSONException { tokener.nextValue(); assertEquals(4, tokenerNextValueCalls); } } openjson-1.0.12/src/test/java/com/github/openjson/performance/000077500000000000000000000000001363767403400243265ustar00rootroot00000000000000openjson-1.0.12/src/test/java/com/github/openjson/performance/ToStringPT.java000066400000000000000000000014511363767403400272070ustar00rootroot00000000000000package com.github.openjson.performance; import com.github.openjson.JSONArray; import com.github.openjson.JSONTokener; import java.io.IOException; import java.io.InputStreamReader; /** * Performance test for toString method. Use manual run from command line or IDE. */ public final class ToStringPT { public static void main(String[] v) throws IOException { JSONArray json = (JSONArray) new JSONTokener(new InputStreamReader(ToStringPT.class.getResourceAsStream("/sample-01.json"))).nextValue(); long t0 = System.currentTimeMillis(); for (int i = 0; i < 300 * 1000; i++) { //noinspection ResultOfMethodCallIgnored json.toString(); } System.out.println("Total time: " + ((System.currentTimeMillis() - t0) / 1000L) + " seconds"); } } openjson-1.0.12/src/test/resources/000077500000000000000000000000001363767403400172235ustar00rootroot00000000000000openjson-1.0.12/src/test/resources/sample-01.json000066400000000000000000000154541363767403400216260ustar00rootroot00000000000000[ { "_id": "58309f3bd307b72ae49a9b23", "index": 0, "guid": "5764ebd8-b333-469e-8d83-4eb5658f1566", "isActive": true, "balance": "$1,099.93", "picture": "http://placehold.it/32x32", "age": 37, "eyeColor": "blue", "name": "Barrera Wilkerson", "gender": "male", "company": "VURBO", "email": "barrerawilkerson@vurbo.com", "phone": "+1 (817) 429-2473", "address": "522 Vanderveer Street, Detroit, Wyoming, 4320", "about": "Et officia aute ullamco magna adipisicing non ut cupidatat cupidatat aliquip. Tempor occaecat ex ad dolore aliquip mollit ea esse ipsum. Est incididunt sunt commodo duis est. Reprehenderit in ut reprehenderit ad culpa ea fugiat et est adipisicing aliquip. Id mollit voluptate qui pariatur officia.\r\n", "registered": "2016-06-29T08:54:14 +07:00", "latitude": -87.548434, "longitude": 64.251242, "tags": [ "aliqua", "ex", "sit", "magna", "dolor", "laborum", "non" ], "friends": [ { "id": 0, "name": "Byers Pratt" }, { "id": 1, "name": "Kennedy Contreras" }, { "id": 2, "name": "Frazier Monroe" } ], "greeting": "Hello, Barrera Wilkerson! You have 3 unread messages.", "favoriteFruit": "banana" }, { "_id": "58309f3b1f506440093a41d1", "index": 1, "guid": "de1a6cc9-f8b3-426e-b68a-cc30e1fff3c1", "isActive": false, "balance": "$3,397.60", "picture": "http://placehold.it/32x32", "age": 32, "eyeColor": "blue", "name": "Trisha Morris", "gender": "female", "company": "AMTAP", "email": "trishamorris@amtap.com", "phone": "+1 (805) 423-3375", "address": "495 Tampa Court, Libertytown, New Hampshire, 5177", "about": "Elit culpa Lorem dolor sit laborum ut ullamco ullamco nostrud reprehenderit adipisicing eiusmod. Aliqua quis dolor esse sint. Dolore in excepteur laborum anim ut consectetur. Nisi officia est eu ex ex id. Ipsum duis ullamco ad ut labore dolor. In amet tempor deserunt ullamco velit eu fugiat.\r\n", "registered": "2015-02-08T06:14:19 +08:00", "latitude": -81.956277, "longitude": 143.685584, "tags": [ "cillum", "ullamco", "magna", "cillum", "voluptate", "magna", "exercitation" ], "friends": [ { "id": 0, "name": "Fuentes Stout" }, { "id": 1, "name": "Violet Vargas" }, { "id": 2, "name": "Schmidt Wilder" } ], "greeting": "Hello, Trisha Morris! You have 4 unread messages.", "favoriteFruit": "strawberry" }, { "_id": "58309f3beaef2f31339b3755", "index": 2, "guid": "0bf387b7-abc2-4828-becc-1269928f7c3d", "isActive": false, "balance": "$1,520.64", "picture": "http://placehold.it/32x32", "age": 37, "eyeColor": "blue", "name": "Deanna Santiago", "gender": "female", "company": "MEGALL", "email": "deannasantiago@megall.com", "phone": "+1 (916) 511-2291", "address": "919 Fayette Street, Homestead, Utah, 8669", "about": "Sit amet ex quis velit irure Lorem non quis aliquip dolor pariatur nulla Lorem officia. Deserunt officia sit velit labore sint nostrud elit aliquip labore ullamco consectetur id amet. Ullamco duis commodo sit incididunt. Fugiat consectetur ad incididunt officia. Sint cillum minim laborum laboris id cillum est exercitation in eiusmod qui.\r\n", "registered": "2015-11-18T08:39:28 +08:00", "latitude": 79.105701, "longitude": -146.901754, "tags": [ "non", "ullamco", "cillum", "ipsum", "amet", "aliqua", "aliquip" ], "friends": [ { "id": 0, "name": "Hanson Anderson" }, { "id": 1, "name": "Pollard Soto" }, { "id": 2, "name": "Barlow Campbell" } ], "greeting": "Hello, Deanna Santiago! You have 7 unread messages.", "favoriteFruit": "apple" }, { "_id": "58309f3b49a68ad01346f27f", "index": 3, "guid": "d29c0dcc-48fb-4ca4-a63b-b47c0e6d6398", "isActive": false, "balance": "$2,069.96", "picture": "http://placehold.it/32x32", "age": 29, "eyeColor": "green", "name": "Brooks Gates", "gender": "male", "company": "TERRAGEN", "email": "brooksgates@terragen.com", "phone": "+1 (875) 483-2224", "address": "562 Noll Street, Kipp, Louisiana, 7659", "about": "Reprehenderit laboris mollit nulla commodo quis laborum commodo. Laborum aliquip laboris officia minim ipsum laborum ipsum reprehenderit quis laboris est sint culpa. Culpa magna aute mollit exercitation.\r\n", "registered": "2016-05-04T10:34:38 +07:00", "latitude": 72.77079, "longitude": -134.291768, "tags": [ "est", "sunt", "laboris", "ea", "proident", "aute", "excepteur" ], "friends": [ { "id": 0, "name": "Roxanne Morgan" }, { "id": 1, "name": "Tamara Kelly" }, { "id": 2, "name": "Cleveland Bush" } ], "greeting": "Hello, Brooks Gates! You have 1 unread messages.", "favoriteFruit": "banana" }, { "_id": "58309f3be746700e9af9a645", "index": 4, "guid": "54382bd6-c476-469d-9e1c-e546f959db51", "isActive": true, "balance": "$2,012.57", "picture": "http://placehold.it/32x32", "age": 40, "eyeColor": "brown", "name": "Jackie Thomas", "gender": "female", "company": "HINWAY", "email": "jackiethomas@hinway.com", "phone": "+1 (843) 470-2096", "address": "910 Emerson Place, Gwynn, Federated States Of Micronesia, 4688", "about": "Id cupidatat laboris elit est eiusmod esse nostrud. Ex commodo nisi voluptate est nisi laborum officia sint incididunt pariatur qui deserunt ullamco. Fugiat proident magna ipsum sit sint id adipisicing sit nostrud labore sit officia. Eiusmod exercitation non enim excepteur amet irure ullamco consectetur cupidatat proident Lorem reprehenderit aliquip. Veniam esse dolor Lorem incididunt proident officia enim in incididunt culpa. Mollit voluptate commodo aliquip anim ipsum nostrud ut labore enim labore qui do minim incididunt. Quis irure proident voluptate nisi qui sunt aute duis irure.\r\n", "registered": "2014-08-03T09:21:43 +07:00", "latitude": 84.871256, "longitude": 2.043339, "tags": [ "tempor", "ut", "deserunt", "esse", "nostrud", "dolore", "ex" ], "friends": [ { "id": 0, "name": "Lois Walters" }, { "id": 1, "name": "Brewer Buchanan" }, { "id": 2, "name": "Mccormick Fleming" } ], "greeting": "Hello, Jackie Thomas! You have 2 unread messages.", "favoriteFruit": "banana" } ]