20 Commits

Author SHA1 Message Date
Sean Leary
7299b201f4 Update pom.xml 2021-03-07 21:11:48 -06:00
Sean Leary
8cc1e9830d Update README.md 2021-03-07 21:09:01 -06:00
Sean Leary
c43e21ae73 Merge pull request #588 from fossterer/563-jsonpointer-do-not-encode-quotes
JSONPointer should not process reverse solidus or double-quote chars in tokens
2021-03-06 10:00:32 -06:00
Shashank Sabniveesu
d6ccc64c79 Closes 563: As never defined in RFC 6901 Section 3, do not handle backslashes (\) and quotes(") as anything special 2021-02-28 16:03:14 -05:00
Sean Leary
7844eb79cd Merge pull request #583 from ek08/fix
Checked the length of key for checker framework
2021-02-01 19:48:06 -06:00
Ehtesham
5b531faa49 Improved the logic for checking the length of key 2021-01-28 15:31:23 +05:30
Ehtesham
31ff8a2291 Checked the length of key for checker framework 2021-01-27 11:35:38 +05:30
Sean Leary
e33f463179 Merge pull request #581 from valfirst/patch-1
Use built-in Gradle shorthand notation for Maven Central repository
2021-01-10 18:35:31 -06:00
Valery Yatsynovich
e77a77e841 Use built-in Gradle shorthand notation for Maven Central repository 2020-12-29 14:16:46 +03:00
Sean Leary
26f48484fd Merge pull request #577 from stranck/master
Added clear() methods to JSONObject and JSONArray
2020-12-07 16:54:18 -06:00
Stranck
efad1d73a7 Added UnitTests
(I hope they works :c)
2020-12-04 04:09:19 +01:00
Stranck
c7130d577a Oops 2020-12-04 01:09:18 +01:00
Stranck
d85eea53bb Update JSONArray.java 2020-12-04 01:07:29 +01:00
Stranck
57ad94ef5e Added clear() methods to JSONObject and JSONArray 2020-12-04 00:49:21 +01:00
Sean Leary
a57eff26d5 Merge pull request #575 from johnjaylward/fix-similar-compare-numbers
Fix similar compare numbers
2020-11-22 15:23:03 -06:00
John J. Aylward
68883b9ff8 update number handling to use new helper method for consistency. 2020-11-19 19:10:08 -05:00
John J. Aylward
11e6b1af7e fixes issue #573 by added specific compare of numeric types 2020-11-19 18:55:49 -05:00
John J. Aylward
e4b76d6588 Add test to demonstrate the issue. See #573 2020-11-19 18:18:27 -05:00
John J. Aylward
3a8193bea4 upgrade junit version 2020-11-19 18:18:02 -05:00
Sean Leary
6bf2692a94 Update README.md 2020-11-15 16:09:50 -06:00
9 changed files with 194 additions and 91 deletions

View File

@@ -3,7 +3,7 @@ JSON in Java [package org.json]
[![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json) [![Maven Central](https://img.shields.io/maven-central/v/org.json/json.svg)](https://mvnrepository.com/artifact/org.json/json)
**[Click here if you just want the latest release jar file.](https://repo1.maven.org/maven2/org/json/json/20200518/json-20200518.jar)** **[Click here if you just want the latest release jar file.](https://repo1.maven.org/maven2/org/json/json/20201115/json-20201115.jar)**
# Overview # Overview
@@ -246,6 +246,8 @@ and artifactId "json". For example:
[https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav) [https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav](https://search.maven.org/search?q=g:org.json%20AND%20a:json&core=gav)
~~~ ~~~
20210307 Recent commits and potentially breaking fix to JSONPointer
20201115 Recent commits and first release after project structure change 20201115 Recent commits and first release after project structure change
20200518 Recent commits and snapshot before project structure change 20200518 Recent commits and snapshot before project structure change

View File

@@ -13,17 +13,14 @@ apply plugin: 'maven-publish'
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral()
maven { maven {
url = uri('https://oss.sonatype.org/content/repositories/snapshots') url = uri('https://oss.sonatype.org/content/repositories/snapshots')
} }
maven {
url = uri('http://repo.maven.apache.org/maven2')
}
} }
dependencies { dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.13.1'
testImplementation 'com.jayway.jsonpath:json-path:2.1.0' testImplementation 'com.jayway.jsonpath:json-path:2.1.0'
testImplementation 'org.mockito:mockito-core:1.9.5' testImplementation 'org.mockito:mockito-core:1.9.5'
} }

View File

@@ -3,7 +3,7 @@
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>v20200429-SNAPSHOT</version> <version>20210307</version>
<packaging>bundle</packaging> <packaging>bundle</packaging>
<name>JSON in Java</name> <name>JSON in Java</name>
@@ -80,7 +80,7 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.12</version> <version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@@ -567,6 +567,14 @@ public class JSONArray implements Iterable<Object> {
return this.myArrayList.size(); return this.myArrayList.size();
} }
/**
* Removes all of the elements from this JSONArray.
* The JSONArray will be empty after this call returns.
*/
public void clear() {
this.myArrayList.clear();
}
/** /**
* Get the optional object value associated with an index. * Get the optional object value associated with an index.
* *
@@ -1374,6 +1382,8 @@ public class JSONArray implements Iterable<Object> {
if (!((JSONArray)valueThis).similar(valueOther)) { if (!((JSONArray)valueThis).similar(valueOther)) {
return false; return false;
} }
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (!valueThis.equals(valueOther)) { } else if (!valueThis.equals(valueOther)) {
return false; return false;
} }

View File

@@ -151,10 +151,10 @@ public class JSONObject {
return "null"; return "null";
} }
} }
/** /**
* Regular Expression Pattern that matches JSON Numbers. This is primarily used for * Regular Expression Pattern that matches JSON Numbers. This is primarily used for
* output to guarantee that we are always writing valid JSON. * output to guarantee that we are always writing valid JSON.
*/ */
static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?");
@@ -175,10 +175,10 @@ public class JSONObject {
* Construct an empty JSONObject. * Construct an empty JSONObject.
*/ */
public JSONObject() { public JSONObject() {
// HashMap is used on purpose to ensure that elements are unordered by // HashMap is used on purpose to ensure that elements are unordered by
// the specification. // the specification.
// JSON tends to be a portable transfer format to allows the container // JSON tends to be a portable transfer format to allows the container
// implementations to rearrange their items for a faster element // implementations to rearrange their items for a faster element
// retrieval based on associative access. // retrieval based on associative access.
// Therefore, an implementation mustn't rely on the order of the item. // Therefore, an implementation mustn't rely on the order of the item.
this.map = new HashMap<String, Object>(); this.map = new HashMap<String, Object>();
@@ -239,9 +239,9 @@ public class JSONObject {
if (c != ':') { if (c != ':') {
throw x.syntaxError("Expected a ':' after a key"); throw x.syntaxError("Expected a ':' after a key");
} }
// Use syntaxError(..) to include error location // Use syntaxError(..) to include error location
if (key != null) { if (key != null) {
// Check if key exists // Check if key exists
if (this.opt(key) != null) { if (this.opt(key) != null) {
@@ -350,11 +350,11 @@ public class JSONObject {
* method from being serialized: * method from being serialized:
* <pre> * <pre>
* &#64;JSONPropertyName("FullName") * &#64;JSONPropertyName("FullName")
* &#64;JSONPropertyIgnore * &#64;JSONPropertyIgnore
* public String getName() { return this.name; } * public String getName() { return this.name; }
* </pre> * </pre>
* <p> * <p>
* *
* @param bean * @param bean
* An object that has getter methods that should be used to make * An object that has getter methods that should be used to make
* a JSONObject. * a JSONObject.
@@ -448,12 +448,12 @@ public class JSONObject {
} }
} }
} }
/** /**
* Constructor to specify an initial capacity of the internal map. Useful for library * Constructor to specify an initial capacity of the internal map. Useful for library
* internal calls where we know, or at least can best guess, how big this JSONObject * internal calls where we know, or at least can best guess, how big this JSONObject
* will be. * will be.
* *
* @param initialCapacity initial capacity of the internal map. * @param initialCapacity initial capacity of the internal map.
*/ */
protected JSONObject(int initialCapacity){ protected JSONObject(int initialCapacity){
@@ -576,7 +576,7 @@ public class JSONObject {
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E> * @param <E>
* Enum Type * Enum Type
* @param clazz * @param clazz
@@ -630,7 +630,7 @@ public class JSONObject {
* A key string. * A key string.
* @return The numeric value. * @return The numeric value.
* @throws JSONException * @throws JSONException
* if the key is not found or if the value cannot * if the key is not found or if the value cannot
* be converted to BigInteger. * be converted to BigInteger.
*/ */
public BigInteger getBigInteger(String key) throws JSONException { public BigInteger getBigInteger(String key) throws JSONException {
@@ -929,7 +929,7 @@ public class JSONObject {
* modify the JSONObject. Use with caution. * modify the JSONObject. Use with caution.
* *
* @see Set#iterator() * @see Set#iterator()
* *
* @return An iterator of the keys. * @return An iterator of the keys.
*/ */
public Iterator<String> keys() { public Iterator<String> keys() {
@@ -950,10 +950,10 @@ public class JSONObject {
/** /**
* Get a set of entries of the JSONObject. These are raw values and may not * Get a set of entries of the JSONObject. These are raw values and may not
* match what is returned by the JSONObject get* and opt* functions. Modifying * match what is returned by the JSONObject get* and opt* functions. Modifying
* the returned EntrySet or the Entry objects contained therein will modify the * the returned EntrySet or the Entry objects contained therein will modify the
* backing JSONObject. This does not return a clone or a read-only view. * backing JSONObject. This does not return a clone or a read-only view.
* *
* Use with caution. * Use with caution.
* *
* @see Map#entrySet() * @see Map#entrySet()
@@ -973,6 +973,14 @@ public class JSONObject {
return this.map.size(); return this.map.size();
} }
/**
* Removes all of the elements from this JSONObject.
* The JSONObject will be empty after this call returns.
*/
public void clear() {
this.map.clear();
}
/** /**
* Check if JSONObject is empty. * Check if JSONObject is empty.
* *
@@ -1039,7 +1047,7 @@ public class JSONObject {
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E> * @param <E>
* Enum Type * Enum Type
* @param clazz * @param clazz
@@ -1054,7 +1062,7 @@ public class JSONObject {
/** /**
* Get the enum value associated with a key. * Get the enum value associated with a key.
* *
* @param <E> * @param <E>
* Enum Type * Enum Type
* @param clazz * @param clazz
@@ -1148,7 +1156,7 @@ public class JSONObject {
* @param val value to convert * @param val value to convert
* @param defaultValue default value to return is the conversion doesn't work or is null. * @param defaultValue default value to return is the conversion doesn't work or is null.
* @return BigDecimal conversion of the original value, or the defaultValue if unable * @return BigDecimal conversion of the original value, or the defaultValue if unable
* to convert. * to convert.
*/ */
static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
if (NULL.equals(val)) { if (NULL.equals(val)) {
@@ -1161,8 +1169,7 @@ public class JSONObject {
return new BigDecimal((BigInteger) val); return new BigDecimal((BigInteger) val);
} }
if (val instanceof Double || val instanceof Float){ if (val instanceof Double || val instanceof Float){
final double d = ((Number) val).doubleValue(); if (!numberIsFinite((Number)val)) {
if(Double.isNaN(d)) {
return defaultValue; return defaultValue;
} }
return new BigDecimal(((Number) val).doubleValue()); return new BigDecimal(((Number) val).doubleValue());
@@ -1199,7 +1206,7 @@ public class JSONObject {
* @param val value to convert * @param val value to convert
* @param defaultValue default value to return is the conversion doesn't work or is null. * @param defaultValue default value to return is the conversion doesn't work or is null.
* @return BigInteger conversion of the original value, or the defaultValue if unable * @return BigInteger conversion of the original value, or the defaultValue if unable
* to convert. * to convert.
*/ */
static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) {
if (NULL.equals(val)) { if (NULL.equals(val)) {
@@ -1212,11 +1219,10 @@ public class JSONObject {
return ((BigDecimal) val).toBigInteger(); return ((BigDecimal) val).toBigInteger();
} }
if (val instanceof Double || val instanceof Float){ if (val instanceof Double || val instanceof Float){
final double d = ((Number) val).doubleValue(); if (!numberIsFinite((Number)val)) {
if(Double.isNaN(d)) {
return defaultValue; return defaultValue;
} }
return new BigDecimal(d).toBigInteger(); return new BigDecimal(((Number) val).doubleValue()).toBigInteger();
} }
if (val instanceof Long || val instanceof Integer if (val instanceof Long || val instanceof Integer
|| val instanceof Short || val instanceof Byte){ || val instanceof Short || val instanceof Byte){
@@ -1224,7 +1230,7 @@ public class JSONObject {
} }
// don't check if it's a string in case of unchecked Number subclasses // don't check if it's a string in case of unchecked Number subclasses
try { try {
// the other opt functions handle implicit conversions, i.e. // the other opt functions handle implicit conversions, i.e.
// jo.put("double",1.1d); // jo.put("double",1.1d);
// jo.optInt("double"); -- will return 1, not an error // jo.optInt("double"); -- will return 1, not an error
// this conversion to BigDecimal then to BigInteger is to maintain // this conversion to BigDecimal then to BigInteger is to maintain
@@ -1398,10 +1404,10 @@ public class JSONObject {
if (val == null) { if (val == null) {
return defaultValue; return defaultValue;
} }
return val.longValue(); return val.longValue();
} }
/** /**
* Get an optional {@link Number} value associated with a key, or <code>null</code> * Get an optional {@link Number} value associated with a key, or <code>null</code>
* if there is no such key or if the value is not a number. If the value is a string, * if there is no such key or if the value is not a number. If the value is a string,
@@ -1436,14 +1442,14 @@ public class JSONObject {
if (val instanceof Number){ if (val instanceof Number){
return (Number) val; return (Number) val;
} }
try { try {
return stringToNumber(val.toString()); return stringToNumber(val.toString());
} catch (Exception e) { } catch (Exception e) {
return defaultValue; return defaultValue;
} }
} }
/** /**
* Get an optional string associated with a key. It returns an empty string * Get an optional string associated with a key. It returns an empty string
* if there is no such key. If the value is not a string and is not null, * if there is no such key. If the value is not a string and is not null,
@@ -1552,7 +1558,7 @@ public class JSONObject {
// if the first letter in the key is not uppercase, then skip. // if the first letter in the key is not uppercase, then skip.
// This is to maintain backwards compatibility before PR406 // This is to maintain backwards compatibility before PR406
// (https://github.com/stleary/JSON-java/pull/406/) // (https://github.com/stleary/JSON-java/pull/406/)
if (Character.isLowerCase(key.charAt(0))) { if (key.length() == 0 || Character.isLowerCase(key.charAt(0))) {
return null; return null;
} }
if (key.length() == 1) { if (key.length() == 1) {
@@ -1729,7 +1735,7 @@ public class JSONObject {
public JSONObject put(String key, double value) throws JSONException { public JSONObject put(String key, double value) throws JSONException {
return this.put(key, Double.valueOf(value)); return this.put(key, Double.valueOf(value));
} }
/** /**
* Put a key/float pair in the JSONObject. * Put a key/float pair in the JSONObject.
* *
@@ -1873,7 +1879,7 @@ public class JSONObject {
} }
/** /**
* Creates a JSONPointer using an initialization string and tries to * Creates a JSONPointer using an initialization string and tries to
* match it to an item within this JSONObject. For example, given a * match it to an item within this JSONObject. For example, given a
* JSONObject initialized with this document: * JSONObject initialized with this document:
* <pre> * <pre>
@@ -1881,13 +1887,13 @@ public class JSONObject {
* "a":{"b":"c"} * "a":{"b":"c"}
* } * }
* </pre> * </pre>
* and this JSONPointer string: * and this JSONPointer string:
* <pre> * <pre>
* "/a/b" * "/a/b"
* </pre> * </pre>
* Then this method will return the String "c". * Then this method will return the String "c".
* A JSONPointerException may be thrown from code called by this method. * A JSONPointerException may be thrown from code called by this method.
* *
* @param jsonPointer string that can be used to create a JSONPointer * @param jsonPointer string that can be used to create a JSONPointer
* @return the item matched by the JSONPointer, otherwise null * @return the item matched by the JSONPointer, otherwise null
*/ */
@@ -1895,7 +1901,7 @@ public class JSONObject {
return query(new JSONPointer(jsonPointer)); return query(new JSONPointer(jsonPointer));
} }
/** /**
* Uses a user initialized JSONPointer and tries to * Uses a user initialized JSONPointer and tries to
* match it to an item within this JSONObject. For example, given a * match it to an item within this JSONObject. For example, given a
* JSONObject initialized with this document: * JSONObject initialized with this document:
* <pre> * <pre>
@@ -1903,24 +1909,24 @@ public class JSONObject {
* "a":{"b":"c"} * "a":{"b":"c"}
* } * }
* </pre> * </pre>
* and this JSONPointer: * and this JSONPointer:
* <pre> * <pre>
* "/a/b" * "/a/b"
* </pre> * </pre>
* Then this method will return the String "c". * Then this method will return the String "c".
* A JSONPointerException may be thrown from code called by this method. * A JSONPointerException may be thrown from code called by this method.
* *
* @param jsonPointer string that can be used to create a JSONPointer * @param jsonPointer string that can be used to create a JSONPointer
* @return the item matched by the JSONPointer, otherwise null * @return the item matched by the JSONPointer, otherwise null
*/ */
public Object query(JSONPointer jsonPointer) { public Object query(JSONPointer jsonPointer) {
return jsonPointer.queryFrom(this); return jsonPointer.queryFrom(this);
} }
/** /**
* Queries and returns a value from this object using {@code jsonPointer}, or * Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key. * returns null if the query fails due to a missing key.
* *
* @param jsonPointer the string representation of the JSON pointer * @param jsonPointer the string representation of the JSON pointer
* @return the queried value or {@code null} * @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
@@ -1928,11 +1934,11 @@ public class JSONObject {
public Object optQuery(String jsonPointer) { public Object optQuery(String jsonPointer) {
return optQuery(new JSONPointer(jsonPointer)); return optQuery(new JSONPointer(jsonPointer));
} }
/** /**
* Queries and returns a value from this object using {@code jsonPointer}, or * Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key. * returns null if the query fails due to a missing key.
* *
* @param jsonPointer The JSON pointer * @param jsonPointer The JSON pointer
* @return the queried value or {@code null} * @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
@@ -2073,6 +2079,8 @@ public class JSONObject {
if (!((JSONArray)valueThis).similar(valueOther)) { if (!((JSONArray)valueThis).similar(valueOther)) {
return false; return false;
} }
} else if (valueThis instanceof Number && valueOther instanceof Number) {
return isNumberSimilar((Number)valueThis, (Number)valueOther);
} else if (!valueThis.equals(valueOther)) { } else if (!valueThis.equals(valueOther)) {
return false; return false;
} }
@@ -2082,10 +2090,59 @@ public class JSONObject {
return false; return false;
} }
} }
/**
* Compares two numbers to see if they are similar.
*
* If either of the numbers are Double or Float instances, then they are checked to have
* a finite value. If either value is not finite (NaN or &#177;infinity), then this
* function will always return false. If both numbers are finite, they are first checked
* to be the same type and implement {@link Comparable}. If they do, then the actual
* {@link Comparable#compareTo(Object)} is called. If they are not the same type, or don't
* implement Comparable, then they are converted to {@link BigDecimal}s. Finally the
* BigDecimal values are compared using {@link BigDecimal#compareTo(BigDecimal)}.
*
* @param l the Left value to compare. Can not be <code>null</code>.
* @param r the right value to compare. Can not be <code>null</code>.
* @return true if the numbers are similar, false otherwise.
*/
static boolean isNumberSimilar(Number l, Number r) {
if (!numberIsFinite(l) || !numberIsFinite(r)) {
// non-finite numbers are never similar
return false;
}
// if the classes are the same and implement Comparable
// then use the built in compare first.
if(l.getClass().equals(r.getClass()) && l instanceof Comparable) {
@SuppressWarnings({ "rawtypes", "unchecked" })
int compareTo = ((Comparable)l).compareTo(r);
return compareTo==0;
}
// BigDecimal should be able to handle all of our number types that we support through
// documentation. Convert to BigDecimal first, then use the Compare method to
// decide equality.
final BigDecimal lBigDecimal = objectToBigDecimal(l, null);
final BigDecimal rBigDecimal = objectToBigDecimal(r, null);
if (lBigDecimal == null || rBigDecimal == null) {
return false;
}
return lBigDecimal.compareTo(rBigDecimal) == 0;
}
private static boolean numberIsFinite(Number n) {
if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) {
return false;
} else if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) {
return false;
}
return true;
}
/** /**
* Tests if the value should be tried as a decimal. It makes no test if there are actual digits. * Tests if the value should be tried as a decimal. It makes no test if there are actual digits.
* *
* @param val value to test * @param val value to test
* @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise. * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise.
*/ */
@@ -2093,12 +2150,12 @@ public class JSONObject {
return val.indexOf('.') > -1 || val.indexOf('e') > -1 return val.indexOf('.') > -1 || val.indexOf('e') > -1
|| val.indexOf('E') > -1 || "-0".equals(val); || val.indexOf('E') > -1 || "-0".equals(val);
} }
/** /**
* Converts a string to a number using the narrowest possible type. Possible * Converts a string to a number using the narrowest possible type. Possible
* returns for this function are BigDecimal, Double, BigInteger, Long, and Integer. * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer.
* When a Double is returned, it should always be a valid Double and not NaN or +-infinity. * When a Double is returned, it should always be a valid Double and not NaN or +-infinity.
* *
* @param val value to convert * @param val value to convert
* @return Number representation of the value. * @return Number representation of the value.
* @throws NumberFormatException thrown if the value is not a valid number. A public * @throws NumberFormatException thrown if the value is not a valid number. A public
@@ -2147,7 +2204,7 @@ public class JSONObject {
// integer representation. // integer representation.
// This will narrow any values to the smallest reasonable Object representation // This will narrow any values to the smallest reasonable Object representation
// (Integer, Long, or BigInteger) // (Integer, Long, or BigInteger)
// BigInteger down conversion: We use a similar bitLenth compare as // BigInteger down conversion: We use a similar bitLenth compare as
// BigInteger#intValueExact uses. Increases GC, but objects hold // BigInteger#intValueExact uses. Increases GC, but objects hold
// only what they need. i.e. Less runtime overhead if the value is // only what they need. i.e. Less runtime overhead if the value is
@@ -2216,18 +2273,8 @@ public class JSONObject {
* If o is a non-finite number. * If o is a non-finite number.
*/ */
public static void testValidity(Object o) throws JSONException { public static void testValidity(Object o) throws JSONException {
if (o != null) { if (o instanceof Number && !numberIsFinite((Number) o)) {
if (o instanceof Double) { throw new JSONException("JSON does not allow non-finite numbers.");
if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
throw new JSONException(
"JSON does not allow non-finite numbers.");
}
} else if (o instanceof Float) {
if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
throw new JSONException(
"JSON does not allow non-finite numbers.");
}
}
} }
} }
@@ -2260,7 +2307,7 @@ public class JSONObject {
* <p><b> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b> * </b>
* *
* @return a printable, displayable, portable, transmittable representation * @return a printable, displayable, portable, transmittable representation
* of the object, beginning with <code>{</code>&nbsp;<small>(left * of the object, beginning with <code>{</code>&nbsp;<small>(left
* brace)</small> and ending with <code>}</code>&nbsp;<small>(right * brace)</small> and ending with <code>}</code>&nbsp;<small>(right
@@ -2277,11 +2324,11 @@ public class JSONObject {
/** /**
* Make a pretty-printed JSON text of this JSONObject. * Make a pretty-printed JSON text of this JSONObject.
* *
* <p>If <pre>{@code indentFactor > 0}</pre> and the {@link JSONObject} * <p>If <pre>{@code indentFactor > 0}</pre> and the {@link JSONObject}
* has only one key, then the object will be output on a single line: * has only one key, then the object will be output on a single line:
* <pre>{@code {"key": 1}}</pre> * <pre>{@code {"key": 1}}</pre>
* *
* <p>If an object has 2 or more keys, then it will be output across * <p>If an object has 2 or more keys, then it will be output across
* multiple lines: <pre>{@code { * multiple lines: <pre>{@code {
* "key1": 1, * "key1": 1,
@@ -2354,7 +2401,7 @@ public class JSONObject {
*/ */
public static Object wrap(Object object) { public static Object wrap(Object object) {
try { try {
if (object == null) { if (NULL.equals(object)) {
return NULL; return NULL;
} }
if (object instanceof JSONObject || object instanceof JSONArray if (object instanceof JSONObject || object instanceof JSONArray
@@ -2459,11 +2506,11 @@ public class JSONObject {
/** /**
* Write the contents of the JSONObject as JSON text to a writer. * Write the contents of the JSONObject as JSON text to a writer.
* *
* <p>If <pre>{@code indentFactor > 0}</pre> and the {@link JSONObject} * <p>If <pre>{@code indentFactor > 0}</pre> and the {@link JSONObject}
* has only one key, then the object will be output on a single line: * has only one key, then the object will be output on a single line:
* <pre>{@code {"key": 1}}</pre> * <pre>{@code {"key": 1}}</pre>
* *
* <p>If an object has 2 or more keys, then it will be output across * <p>If an object has 2 or more keys, then it will be output across
* multiple lines: <pre>{@code { * multiple lines: <pre>{@code {
* "key1": 1, * "key1": 1,
@@ -2565,7 +2612,7 @@ public class JSONObject {
} }
return results; return results;
} }
/** /**
* Create a new JSONException in a common format for incorrect conversions. * Create a new JSONException in a common format for incorrect conversions.
* @param key name of the key * @param key name of the key
@@ -2581,7 +2628,7 @@ public class JSONObject {
"JSONObject[" + quote(key) + "] is not a " + valueType + "." "JSONObject[" + quote(key) + "] is not a " + valueType + "."
, cause); , cause);
} }
/** /**
* Create a new JSONException in a common format for incorrect conversions. * Create a new JSONException in a common format for incorrect conversions.
* @param key name of the key * @param key name of the key

View File

@@ -187,10 +187,11 @@ public class JSONPointer {
this.refTokens = new ArrayList<String>(refTokens); this.refTokens = new ArrayList<String>(refTokens);
} }
/**
* @see https://tools.ietf.org/html/rfc6901#section-3
*/
private static String unescape(String token) { private static String unescape(String token) {
return token.replace("~1", "/").replace("~0", "~") return token.replace("~1", "/").replace("~0", "~");
.replace("\\\"", "\"")
.replace("\\\\", "\\");
} }
/** /**
@@ -263,16 +264,15 @@ public class JSONPointer {
/** /**
* Escapes path segment values to an unambiguous form. * Escapes path segment values to an unambiguous form.
* The escape char to be inserted is '~'. The chars to be escaped * The escape char to be inserted is '~'. The chars to be escaped
* are ~, which maps to ~0, and /, which maps to ~1. Backslashes * are ~, which maps to ~0, and /, which maps to ~1.
* and double quote chars are also escaped.
* @param token the JSONPointer segment value to be escaped * @param token the JSONPointer segment value to be escaped
* @return the escaped value for the token * @return the escaped value for the token
*
* @see https://tools.ietf.org/html/rfc6901#section-3
*/ */
private static String escape(String token) { private static String escape(String token) {
return token.replace("~", "~0") return token.replace("~", "~0")
.replace("/", "~1") .replace("/", "~1");
.replace("\\", "\\\\")
.replace("\"", "\\\"");
} }
/** /**

View File

@@ -1254,4 +1254,19 @@ public class JSONArrayTest {
assertEquals("index " + i + " are equal", a1.get(i), a2.get(i)); assertEquals("index " + i + " are equal", a1.get(i), a2.get(i));
} }
} }
/**
* Tests if calling JSONArray clear() method actually makes the JSONArray empty
*/
@Test(expected = JSONException.class)
public void jsonArrayClearMethodTest() {
//Adds random stuff to the JSONArray
JSONArray jsonArray = new JSONArray();
jsonArray.put(123);
jsonArray.put("456");
jsonArray.put(new JSONArray());
jsonArray.clear(); //Clears the JSONArray
assertTrue("expected jsonArray.length() == 0", jsonArray.length() == 0); //Check if its length is 0
jsonArray.getInt(0); //Should throws org.json.JSONException: JSONArray[0] not found
}
} }

View File

@@ -115,10 +115,17 @@ public class JSONObjectTest {
.put("key2", 2) .put("key2", 2)
.put("key3", new String(string1)); .put("key3", new String(string1));
assertFalse("Should eval to false", obj1.similar(obj2)); JSONObject obj4 = new JSONObject()
.put("key1", "abc")
.put("key2", 2.0)
.put("key3", new String(string1));
assertFalse("Should eval to false", obj1.similar(obj2));
assertTrue("Should eval to true", obj1.similar(obj3)); assertTrue("Should eval to true", obj1.similar(obj3));
assertTrue("Should eval to true", obj1.similar(obj4));
} }
@Test @Test
@@ -3208,4 +3215,19 @@ public class JSONObjectTest {
assertNotNull("'empty_json_array' should be an array", jsonObject.getJSONArray("empty_json_array")); assertNotNull("'empty_json_array' should be an array", jsonObject.getJSONArray("empty_json_array"));
assertEquals("'empty_json_array' should have a length of 0", 0, jsonObject.getJSONArray("empty_json_array").length()); assertEquals("'empty_json_array' should have a length of 0", 0, jsonObject.getJSONArray("empty_json_array").length());
} }
/**
* Tests if calling JSONObject clear() method actually makes the JSONObject empty
*/
@Test(expected = JSONException.class)
public void jsonObjectClearMethodTest() {
//Adds random stuff to the JSONObject
JSONObject jsonObject = new JSONObject();
jsonObject.put("key1", 123);
jsonObject.put("key2", "456");
jsonObject.put("key3", new JSONObject());
jsonObject.clear(); //Clears the JSONObject
assertTrue("expected jsonObject.length() == 0", jsonObject.length() == 0); //Check if its length is 0
jsonObject.getInt("key1"); //Should throws org.json.JSONException: JSONObject["asd"] not found
}
} }

View File

@@ -117,14 +117,24 @@ public class JSONPointerTest {
assertSame(document.get("m~n"), query("/m~0n")); assertSame(document.get("m~n"), query("/m~0n"));
} }
/**
* We pass backslashes as-is
*
* @see https://tools.ietf.org/html/rfc6901#section-3
*/
@Test @Test
public void backslashEscaping() { public void backslashHandling() {
assertSame(document.get("i\\j"), query("/i\\\\j")); assertSame(document.get("i\\j"), query("/i\\j"));
} }
/**
* We pass quotations as-is
*
* @see https://tools.ietf.org/html/rfc6901#section-3
*/
@Test @Test
public void quotationEscaping() { public void quotationHandling() {
assertSame(document.get("k\"l"), query("/k\\\\\\\"l")); assertSame(document.get("k\"l"), query("/k\"l"));
} }
@Test @Test
@@ -189,7 +199,7 @@ public class JSONPointerTest {
.append("\"") .append("\"")
.append(0) .append(0)
.build(); .build();
assertEquals("/obj/other~0key/another~1key/\\\"/0", pointer.toString()); assertEquals("/obj/other~0key/another~1key/\"/0", pointer.toString());
} }
@Test @Test