From a63fa0306213e835c21cc7432220394ed2798d2d Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Tue, 2 Oct 2018 12:37:15 -0400 Subject: [PATCH 1/5] * Fixes opt/getBigDecimal to be consistent * Performance: Updates JSONWriter to use a regex to decide if writing as a number is best. --- JSONArray.java | 64 +++++++--------------------------------- JSONObject.java | 78 ++++++++++++++++++++++++++++++++++--------------- JSONWriter.java | 12 +++----- 3 files changed, 69 insertions(+), 85 deletions(-) diff --git a/JSONArray.java b/JSONArray.java index fbc1a0f73..dd2204667 100644 --- a/JSONArray.java +++ b/JSONArray.java @@ -345,12 +345,12 @@ public > E getEnum(Class clazz, int index) throws JSONExcep */ public BigDecimal getBigDecimal (int index) throws JSONException { Object object = this.get(index); - try { - return new BigDecimal(object.toString()); - } catch (Exception e) { + BigDecimal val = JSONObject.objectToBigDecimal(object, null); + if(val == null) { throw new JSONException("JSONArray[" + index + - "] could not convert to BigDecimal.", e); + "] could not convert to BigDecimal ("+ object + ")."); } + return val; } /** @@ -365,12 +365,12 @@ public BigDecimal getBigDecimal (int index) throws JSONException { */ public BigInteger getBigInteger (int index) throws JSONException { Object object = this.get(index); - try { - return new BigInteger(object.toString()); - } catch (Exception e) { + BigInteger val = JSONObject.objectToBigInteger(object, null); + if(val == null) { throw new JSONException("JSONArray[" + index + - "] could not convert to BigInteger.", e); + "] could not convert to BigDecimal ("+ object + ")."); } + return val; } /** @@ -739,31 +739,7 @@ public > E optEnum(Class clazz, int index, E defaultValue) */ public BigInteger optBigInteger(int index, BigInteger defaultValue) { Object val = this.opt(index); - if (JSONObject.NULL.equals(val)) { - return defaultValue; - } - if (val instanceof BigInteger){ - return (BigInteger) val; - } - if (val instanceof BigDecimal){ - return ((BigDecimal) val).toBigInteger(); - } - if (val instanceof Double || val instanceof Float){ - return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); - } - if (val instanceof Long || val instanceof Integer - || val instanceof Short || val instanceof Byte){ - return BigInteger.valueOf(((Number) val).longValue()); - } - try { - final String valStr = val.toString(); - if(JSONObject.isDecimalNotation(valStr)) { - return new BigDecimal(valStr).toBigInteger(); - } - return new BigInteger(valStr); - } catch (Exception e) { - return defaultValue; - } + return JSONObject.objectToBigInteger(val, defaultValue); } /** @@ -779,27 +755,7 @@ public BigInteger optBigInteger(int index, BigInteger defaultValue) { */ public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { Object val = this.opt(index); - if (JSONObject.NULL.equals(val)) { - return defaultValue; - } - if (val instanceof BigDecimal){ - return (BigDecimal) val; - } - if (val instanceof BigInteger){ - return new BigDecimal((BigInteger) val); - } - if (val instanceof Double || val instanceof Float){ - return new BigDecimal(((Number) val).doubleValue()); - } - if (val instanceof Long || val instanceof Integer - || val instanceof Short || val instanceof Byte){ - return new BigDecimal(((Number) val).longValue()); - } - try { - return new BigDecimal(val.toString()); - } catch (Exception e) { - return defaultValue; - } + return JSONObject.objectToBigDecimal(val, defaultValue); } /** diff --git a/JSONObject.java b/JSONObject.java index 8deb6bae5..1a9b9de8a 100644 --- a/JSONObject.java +++ b/JSONObject.java @@ -45,6 +45,7 @@ of this software and associated documentation files (the "Software"), to deal import java.util.Map.Entry; import java.util.ResourceBundle; import java.util.Set; +import java.util.regex.Pattern; /** * A JSONObject is an unordered collection of name/value pairs. Its external @@ -150,6 +151,12 @@ public String toString() { return "null"; } } + + /** + * Regular Expression Pattern that matches JSON Numbers. This is primarily used for + * output to guarantee that we are always writing valid JSON. + */ + static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); /** * The map where the JSONObject's properties are kept. @@ -630,16 +637,19 @@ public boolean getBoolean(String key) throws JSONException { */ public BigInteger getBigInteger(String key) throws JSONException { Object object = this.get(key); - try { - return new BigInteger(object.toString()); - } catch (Exception e) { - throw new JSONException("JSONObject[" + quote(key) - + "] could not be converted to BigInteger.", e); + BigInteger ret = objectToBigInteger(object, null); + if (ret != null) { + return ret; } + throw new JSONException("JSONObject[" + quote(key) + + "] could not be converted to BigInteger (" + object + ")."); } /** - * Get the BigDecimal value associated with a key. + * Get the BigDecimal value associated with a key. If the value is float or + * double, the the {@link BigDecimal#BigDecimal(double)} constructor will + * be used. See notes on the constructor for conversion issues that may + * arise. * * @param key * A key string. @@ -650,15 +660,12 @@ public BigInteger getBigInteger(String key) throws JSONException { */ public BigDecimal getBigDecimal(String key) throws JSONException { Object object = this.get(key); - if (object instanceof BigDecimal) { - return (BigDecimal)object; - } - try { - return new BigDecimal(object.toString()); - } catch (Exception e) { - throw new JSONException("JSONObject[" + quote(key) - + "] could not be converted to BigDecimal.", e); + BigDecimal ret = objectToBigDecimal(object, null); + if (ret != null) { + return ret; } + throw new JSONException("JSONObject[" + quote(key) + + "] could not be converted to BigDecimal (" + object + ")."); } /** @@ -968,7 +975,7 @@ public int length() { * @return true if JSONObject is empty, otherwise false. */ public boolean isEmpty() { - return map.isEmpty(); + return this.map.isEmpty(); } /** @@ -1113,7 +1120,10 @@ public boolean optBoolean(String key, boolean defaultValue) { /** * Get an optional BigDecimal associated with a key, or the defaultValue if * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. + * string, an attempt will be made to evaluate it as a number. If the value + * is float or double, then the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. * * @param key * A key string. @@ -1123,6 +1133,15 @@ public boolean optBoolean(String key, boolean defaultValue) { */ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { Object val = this.opt(key); + return objectToBigDecimal(val, defaultValue); + } + + /** + * @param defaultValue + * @param val + * @return + */ + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { if (NULL.equals(val)) { return defaultValue; } @@ -1133,6 +1152,10 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { return new BigDecimal((BigInteger) val); } if (val instanceof Double || val instanceof Float){ + final double d = ((Number) val).doubleValue(); + if(Double.isNaN(d)) { + return defaultValue; + } return new BigDecimal(((Number) val).doubleValue()); } if (val instanceof Long || val instanceof Integer @@ -1160,6 +1183,15 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { */ public BigInteger optBigInteger(String key, BigInteger defaultValue) { Object val = this.opt(key); + return objectToBigInteger(val, defaultValue); + } + + /** + * @param defaultValue + * @param val + * @return + */ + static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { if (NULL.equals(val)) { return defaultValue; } @@ -1170,7 +1202,11 @@ public BigInteger optBigInteger(String key, BigInteger defaultValue) { return ((BigDecimal) val).toBigInteger(); } if (val instanceof Double || val instanceof Float){ - return new BigDecimal(((Number) val).doubleValue()).toBigInteger(); + final double d = ((Number) val).doubleValue(); + if(Double.isNaN(d)) { + return defaultValue; + } + return new BigDecimal(d).toBigInteger(); } if (val instanceof Long || val instanceof Integer || val instanceof Short || val instanceof Byte){ @@ -2414,13 +2450,9 @@ static final Writer writeValue(Writer writer, Object value, } else if (value instanceof Number) { // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary final String numberAsString = numberToString((Number) value); - try { - // Use the BigDecimal constructor for its parser to validate the format. - @SuppressWarnings("unused") - BigDecimal testNum = new BigDecimal(numberAsString); - // Close enough to a JSON number that we will use it unquoted + if(NUMBER_PATTERN.matcher(numberAsString).matches()) { writer.write(numberAsString); - } catch (NumberFormatException ex){ + } else { // The Number value is not a valid JSON number. // Instead we will quote it as a string quote(numberAsString, writer); diff --git a/JSONWriter.java b/JSONWriter.java index e487781ca..8ef60842d 100644 --- a/JSONWriter.java +++ b/JSONWriter.java @@ -340,17 +340,13 @@ public static String valueToString(Object value) throws JSONException { if (value instanceof Number) { // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex final String numberAsString = JSONObject.numberToString((Number) value); - try { - // Use the BigDecimal constructor for it's parser to validate the format. - @SuppressWarnings("unused") - BigDecimal unused = new BigDecimal(numberAsString); + if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) { // Close enough to a JSON number that we will return it unquoted return numberAsString; - } catch (NumberFormatException ex){ - // The Number value is not a valid JSON number. - // Instead we will quote it as a string - return JSONObject.quote(numberAsString); } + // The Number value is not a valid JSON number. + // Instead we will quote it as a string + return JSONObject.quote(numberAsString); } if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) { From bc347d2c19311db90925f187f6b6a5ae422e3af2 Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Tue, 2 Oct 2018 13:14:22 -0400 Subject: [PATCH 2/5] cleanup of minor warnings --- JSONArray.java | 2 +- JSONPointer.java | 4 ++-- JSONWriter.java | 1 - XMLTokener.java | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/JSONArray.java b/JSONArray.java index dd2204667..931443894 100644 --- a/JSONArray.java +++ b/JSONArray.java @@ -1491,7 +1491,7 @@ public List toList() { * @return true if JSONArray is empty, otherwise false. */ public boolean isEmpty() { - return myArrayList.isEmpty(); + return this.myArrayList.isEmpty(); } } diff --git a/JSONPointer.java b/JSONPointer.java index fc0b04b7c..df06f22ed 100644 --- a/JSONPointer.java +++ b/JSONPointer.java @@ -233,8 +233,8 @@ private Object readByIndexToken(Object current, String indexToken) throws JSONPo int index = Integer.parseInt(indexToken); JSONArray currentArr = (JSONArray) current; if (index >= currentArr.length()) { - throw new JSONPointerException(format("index %d is out of bounds - the array has %d elements", index, - currentArr.length())); + throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, + Integer.valueOf(currentArr.length()))); } try { return currentArr.get(index); diff --git a/JSONWriter.java b/JSONWriter.java index 8ef60842d..a30a22228 100644 --- a/JSONWriter.java +++ b/JSONWriter.java @@ -1,7 +1,6 @@ package org.json; import java.io.IOException; -import java.math.BigDecimal; import java.util.Collection; import java.util.Map; diff --git a/XMLTokener.java b/XMLTokener.java index 50e3acce3..8490becac 100644 --- a/XMLTokener.java +++ b/XMLTokener.java @@ -135,7 +135,7 @@ public Object nextContent() throws JSONException { * @return A Character or an entity String if the entity is not recognized. * @throws JSONException If missing ';' in XML entity. */ - public Object nextEntity(char ampersand) throws JSONException { + public Object nextEntity(@SuppressWarnings("unused") char ampersand) throws JSONException { StringBuilder sb = new StringBuilder(); for (;;) { char c = next(); From 30c1bd16bac7ceb41bce719b44faf22b465c9907 Mon Sep 17 00:00:00 2001 From: "John J. Aylward" Date: Tue, 2 Oct 2018 13:38:19 -0400 Subject: [PATCH 3/5] fix javadoc --- JSONArray.java | 53 +++++++++++++++++++++++++++--------------- JSONObject.java | 61 ++++++++++++++++++++++++++++++------------------- 2 files changed, 73 insertions(+), 41 deletions(-) diff --git a/JSONArray.java b/JSONArray.java index 931443894..b7ae56520 100644 --- a/JSONArray.java +++ b/JSONArray.java @@ -180,10 +180,16 @@ public JSONArray(Collection collection) { } /** - * Construct a JSONArray from an array + * Construct a JSONArray from an array. + * + * @param array + * Array. If the parameter passed is null, or not an array, an + * exception will be thrown. * * @throws JSONException - * If not an array or if an array value is non-finite number. + * If not an array or if an array value is non-finite number. + * @throws NullPointerException + * Thrown if the array parameter is null. */ public JSONArray(Object array) throws JSONException { this(); @@ -310,17 +316,19 @@ public Number getNumber(int index) throws JSONException { } /** - * Get the enum value associated with an index. - * - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. - * @return The enum value at the index location - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an enum. - */ + * Get the enum value associated with an index. + * + * @param + * Enum Type + * @param clazz + * The type of enum to retrieve. + * @param index + * The index must be between 0 and length() - 1. + * @return The enum value at the index location + * @throws JSONException + * if the key is not found or if the value cannot be converted + * to an enum. + */ public > E getEnum(Class clazz, int index) throws JSONException { E val = optEnum(clazz, index); if(val==null) { @@ -334,7 +342,10 @@ public > E getEnum(Class clazz, int index) throws JSONExcep } /** - * Get the BigDecimal value associated with an index. + * Get the BigDecimal value associated with an index. If the value is float + * or double, the the {@link BigDecimal#BigDecimal(double)} constructor + * will be used. See notes on the constructor for conversion issues that + * may arise. * * @param index * The index must be between 0 and length() - 1. @@ -683,6 +694,8 @@ public int optInt(int index, int defaultValue) { /** * Get the enum value associated with a key. * + * @param + * Enum Type * @param clazz * The type of enum to retrieve. * @param index @@ -696,6 +709,8 @@ public > E optEnum(Class clazz, int index) { /** * Get the enum value associated with a key. * + * @param + * Enum Type * @param clazz * The type of enum to retrieve. * @param index @@ -725,7 +740,6 @@ public > E optEnum(Class clazz, int index, E defaultValue) } } - /** * Get the optional BigInteger value associated with an index. The * defaultValue is returned if there is no value for the index, or if the @@ -745,7 +759,10 @@ public BigInteger optBigInteger(int index, BigInteger defaultValue) { /** * Get the optional BigDecimal value associated with an index. The * defaultValue is returned if there is no value for the index, or if the - * value is not a number and cannot be converted to a number. + * value is not a number and cannot be converted to a number. If the value + * is float or double, the the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. * * @param index * The index must be between 0 and length() - 1. @@ -1192,8 +1209,8 @@ public Object query(String jsonPointer) { } /** - * Uses a uaer initialized JSONPointer and tries to - * match it to an item whithin this JSONArray. For example, given a + * Uses a user initialized JSONPointer and tries to + * match it to an item within this JSONArray. For example, given a * JSONArray initialized with this document: *
      * [
diff --git a/JSONObject.java b/JSONObject.java
index 1a9b9de8a..67ae9c76a 100644
--- a/JSONObject.java
+++ b/JSONObject.java
@@ -576,17 +576,19 @@ public Object get(String key) throws JSONException {
     }
 
     /**
-    * Get the enum value associated with a key.
-    * 
-    * @param clazz
-    *           The type of enum to retrieve.
-    * @param key
-    *           A key string.
-    * @return The enum value associated with the key
-    * @throws JSONException
-    *             if the key is not found or if the value cannot be converted
-    *             to an enum.
-    */
+     * Get the enum value associated with a key.
+     * 
+     * @param 
+     *            Enum Type
+     * @param clazz
+     *           The type of enum to retrieve.
+     * @param key
+     *           A key string.
+     * @return The enum value associated with the key
+     * @throws JSONException
+     *             if the key is not found or if the value cannot be converted
+     *             to an enum.
+     */
     public > E getEnum(Class clazz, String key) throws JSONException {
         E val = optEnum(clazz, key);
         if(val==null) {
@@ -814,6 +816,8 @@ public long getLong(String key) throws JSONException {
     /**
      * Get an array of field names from a JSONObject.
      *
+     * @param jo
+     *            JSON object
      * @return An array of field names, or null if there are no names.
      */
     public static String[] getNames(JSONObject jo) {
@@ -824,8 +828,10 @@ public static String[] getNames(JSONObject jo) {
     }
 
     /**
-     * Get an array of field names from an Object.
+     * Get an array of public field names from an Object.
      *
+     * @param object
+     *            object to read
      * @return An array of field names, or null if there are no names.
      */
     public static String[] getNames(Object object) {
@@ -1036,6 +1042,8 @@ public Object opt(String key) {
     /**
      * Get the enum value associated with a key.
      * 
+     * @param 
+     *            Enum Type
      * @param clazz
      *            The type of enum to retrieve.
      * @param key
@@ -1049,6 +1057,8 @@ public > E optEnum(Class clazz, String key) {
     /**
      * Get the enum value associated with a key.
      * 
+     * @param 
+     *            Enum Type
      * @param clazz
      *            The type of enum to retrieve.
      * @param key
@@ -1137,9 +1147,10 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
     }
 
     /**
-     * @param defaultValue
-     * @param val
-     * @return
+     * @param val value to convert
+     * @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
+     *          to convert. 
      */
     static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
         if (NULL.equals(val)) {
@@ -1187,9 +1198,10 @@ public BigInteger optBigInteger(String key, BigInteger defaultValue) {
     }
 
     /**
-     * @param defaultValue
-     * @param val
-     * @return
+     * @param val value to convert
+     * @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
+     *          to convert. 
      */
     static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) {
         if (NULL.equals(val)) {
@@ -1859,8 +1871,10 @@ public JSONObject put(String key, Object value) throws JSONException {
      * are both non-null, and only if there is not already a member with that
      * name.
      *
-     * @param key string
-     * @param value object
+     * @param key
+     *            key to insert into
+     * @param value
+     *            value to insert
      * @return this.
      * @throws JSONException
      *             if the key is a duplicate
@@ -1971,9 +1985,10 @@ public Object optQuery(JSONPointer jsonPointer) {
 
     /**
      * Produce a string in double quotes with backslash sequences in all the
-     * right places. A backslash will be inserted within 
Date: Tue, 2 Oct 2018 15:33:33 -0400
Subject: [PATCH 4/5] remove unneeded casts

---
 JSONWriter.java | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/JSONWriter.java b/JSONWriter.java
index a30a22228..19f2dc816 100644
--- a/JSONWriter.java
+++ b/JSONWriter.java
@@ -325,14 +325,14 @@ public static String valueToString(Object value) throws JSONException {
             return "null";
         }
         if (value instanceof JSONString) {
-            Object object;
+            String object;
             try {
                 object = ((JSONString) value).toJSONString();
             } catch (Exception e) {
                 throw new JSONException(e);
             }
-            if (object instanceof String) {
-                return (String) object;
+            if (object != null) {
+                return object;
             }
             throw new JSONException("Bad value from toJSONString: " + object);
         }

From 34cfe6df14d80bc9015e07257373b81b6f47afa5 Mon Sep 17 00:00:00 2001
From: "John J. Aylward" 
Date: Thu, 4 Oct 2018 16:02:14 -0400
Subject: [PATCH 5/5] removes duplicate code in number getters

---
 JSONArray.java  | 122 ++++++++++++------------------------------
 JSONObject.java | 137 ++++++++++++++----------------------------------
 2 files changed, 72 insertions(+), 187 deletions(-)

diff --git a/JSONArray.java b/JSONArray.java
index b7ae56520..537abb134 100644
--- a/JSONArray.java
+++ b/JSONArray.java
@@ -263,13 +263,7 @@ public boolean getBoolean(int index) throws JSONException {
      *             to a number.
      */
     public double getDouble(int index) throws JSONException {
-        Object object = this.get(index);
-        try {
-            return object instanceof Number ? ((Number) object).doubleValue()
-                    : Double.parseDouble((String) object);
-        } catch (Exception e) {
-            throw new JSONException("JSONArray[" + index + "] is not a number.", e);
-        }
+        return this.getNumber(index).doubleValue();
     }
 
     /**
@@ -283,14 +277,7 @@ public double getDouble(int index) throws JSONException {
      *             object and cannot be converted to a number.
      */
     public float getFloat(int index) throws JSONException {
-        Object object = this.get(index);
-        try {
-            return object instanceof Number ? ((Number) object).floatValue()
-                    : Float.parseFloat(object.toString());
-        } catch (Exception e) {
-            throw new JSONException("JSONArray[" + index
-                    + "] is not a number.", e);
-        }
+        return this.getNumber(index).floatValue();
     }
 
     /**
@@ -394,13 +381,7 @@ public BigInteger getBigInteger (int index) throws JSONException {
      *             If the key is not found or if the value is not a number.
      */
     public int getInt(int index) throws JSONException {
-        Object object = this.get(index);
-        try {
-            return object instanceof Number ? ((Number) object).intValue()
-                    : Integer.parseInt((String) object);
-        } catch (Exception e) {
-            throw new JSONException("JSONArray[" + index + "] is not a number.", e);
-        }
+        return this.getNumber(index).intValue();
     }
 
     /**
@@ -450,13 +431,7 @@ public JSONObject getJSONObject(int index) throws JSONException {
      *             to a number.
      */
     public long getLong(int index) throws JSONException {
-        Object object = this.get(index);
-        try {
-            return object instanceof Number ? ((Number) object).longValue()
-                    : Long.parseLong((String) object);
-        } catch (Exception e) {
-            throw new JSONException("JSONArray[" + index + "] is not a number.", e);
-        }
+        return this.getNumber(index).longValue();
     }
 
     /**
@@ -500,13 +475,16 @@ public boolean isNull(int index) {
      */
     public String join(String separator) throws JSONException {
         int len = this.length();
-        StringBuilder sb = new StringBuilder();
+        if (len == 0) {
+            return "";
+        }
+        
+        StringBuilder sb = new StringBuilder(
+                   JSONObject.valueToString(this.myArrayList.get(0)));
 
-        for (int i = 0; i < len; i += 1) {
-            if (i > 0) {
-                sb.append(separator);
-            }
-            sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
+        for (int i = 1; i < len; i++) {
+            sb.append(separator)
+              .append(JSONObject.valueToString(this.myArrayList.get(i)));
         }
         return sb.toString();
     }
@@ -589,21 +567,15 @@ public double optDouble(int index) {
      * @return The value.
      */
     public double optDouble(int index, double defaultValue) {
-        Object val = this.opt(index);
-        if (JSONObject.NULL.equals(val)) {
+        final Number val = this.optNumber(index, null);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).doubleValue();
-        }
-        if (val instanceof String) {
-            try {
-                return Double.parseDouble((String) val);
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        final double doubleValue = val.doubleValue();
+        // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
+        // return defaultValue;
+        // }
+        return doubleValue;
     }
 
     /**
@@ -631,21 +603,15 @@ public float optFloat(int index) {
      * @return The value.
      */
     public float optFloat(int index, float defaultValue) {
-        Object val = this.opt(index);
-        if (JSONObject.NULL.equals(val)) {
+        final Number val = this.optNumber(index, null);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).floatValue();
-        }
-        if (val instanceof String) {
-            try {
-                return Float.parseFloat((String) val);
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        final float floatValue = val.floatValue();
+        // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
+        // return floatValue;
+        // }
+        return floatValue;
     }
 
     /**
@@ -673,22 +639,11 @@ public int optInt(int index) {
      * @return The value.
      */
     public int optInt(int index, int defaultValue) {
-        Object val = this.opt(index);
-        if (JSONObject.NULL.equals(val)) {
+        final Number val = this.optNumber(index, null);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).intValue();
-        }
-        
-        if (val instanceof String) {
-            try {
-                return new BigDecimal(val.toString()).intValue();
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        return val.intValue();
     }
 
     /**
@@ -827,22 +782,11 @@ public long optLong(int index) {
      * @return The value.
      */
     public long optLong(int index, long defaultValue) {
-        Object val = this.opt(index);
-        if (JSONObject.NULL.equals(val)) {
+        final Number val = this.optNumber(index, null);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).longValue();
-        }
-        
-        if (val instanceof String) {
-            try {
-                return new BigDecimal(val.toString()).longValue();
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        return val.longValue();
     }
 
     /**
diff --git a/JSONObject.java b/JSONObject.java
index 67ae9c76a..a1ed4901c 100644
--- a/JSONObject.java
+++ b/JSONObject.java
@@ -681,14 +681,7 @@ public BigDecimal getBigDecimal(String key) throws JSONException {
      *             object and cannot be converted to a number.
      */
     public double getDouble(String key) throws JSONException {
-        Object object = this.get(key);
-        try {
-            return object instanceof Number ? ((Number) object).doubleValue()
-                    : Double.parseDouble(object.toString());
-        } catch (Exception e) {
-            throw new JSONException("JSONObject[" + quote(key)
-                    + "] is not a number.", e);
-        }
+        return this.getNumber(key).doubleValue();
     }
 
     /**
@@ -702,14 +695,7 @@ public double getDouble(String key) throws JSONException {
      *             object and cannot be converted to a number.
      */
     public float getFloat(String key) throws JSONException {
-        Object object = this.get(key);
-        try {
-            return object instanceof Number ? ((Number) object).floatValue()
-                    : Float.parseFloat(object.toString());
-        } catch (Exception e) {
-            throw new JSONException("JSONObject[" + quote(key)
-                    + "] is not a number.", e);
-        }
+        return this.getNumber(key).floatValue();
     }
 
     /**
@@ -746,14 +732,7 @@ public Number getNumber(String key) throws JSONException {
      *             to an integer.
      */
     public int getInt(String key) throws JSONException {
-        Object object = this.get(key);
-        try {
-            return object instanceof Number ? ((Number) object).intValue()
-                    : Integer.parseInt((String) object);
-        } catch (Exception e) {
-            throw new JSONException("JSONObject[" + quote(key)
-                    + "] is not an int.", e);
-        }
+        return this.getNumber(key).intValue();
     }
 
     /**
@@ -803,14 +782,7 @@ public JSONObject getJSONObject(String key) throws JSONException {
      *             to a long.
      */
     public long getLong(String key) throws JSONException {
-        Object object = this.get(key);
-        try {
-            return object instanceof Number ? ((Number) object).longValue()
-                    : Long.parseLong((String) object);
-        } catch (Exception e) {
-            throw new JSONException("JSONObject[" + quote(key)
-                    + "] is not a long.", e);
-        }
+        return this.getNumber(key).longValue();
     }
 
     /**
@@ -1266,21 +1238,15 @@ public double optDouble(String key) {
      * @return An object which is the value.
      */
     public double optDouble(String key, double defaultValue) {
-        Object val = this.opt(key);
-        if (NULL.equals(val)) {
+        Number val = this.optNumber(key);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).doubleValue();
-        }
-        if (val instanceof String) {
-            try {
-                return Double.parseDouble((String) val);
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        final double doubleValue = val.doubleValue();
+        // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) {
+        // return defaultValue;
+        // }
+        return doubleValue;
     }
 
     /**
@@ -1308,21 +1274,15 @@ public float optFloat(String key) {
      * @return The value.
      */
     public float optFloat(String key, float defaultValue) {
-        Object val = this.opt(key);
-        if (JSONObject.NULL.equals(val)) {
+        Number val = this.optNumber(key);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).floatValue();
-        }
-        if (val instanceof String) {
-            try {
-                return Float.parseFloat((String) val);
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        final float floatValue = val.floatValue();
+        // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) {
+        // return defaultValue;
+        // }
+        return floatValue;
     }
 
     /**
@@ -1350,22 +1310,11 @@ public int optInt(String key) {
      * @return An object which is the value.
      */
     public int optInt(String key, int defaultValue) {
-        Object val = this.opt(key);
-        if (NULL.equals(val)) {
+        final Number val = this.optNumber(key, null);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).intValue();
-        }
-        
-        if (val instanceof String) {
-            try {
-                return new BigDecimal((String) val).intValue();
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        return val.intValue();
     }
 
     /**
@@ -1419,22 +1368,12 @@ public long optLong(String key) {
      * @return An object which is the value.
      */
     public long optLong(String key, long defaultValue) {
-        Object val = this.opt(key);
-        if (NULL.equals(val)) {
+        final Number val = this.optNumber(key, null);
+        if (val == null) {
             return defaultValue;
         }
-        if (val instanceof Number){
-            return ((Number) val).longValue();
-        }
         
-        if (val instanceof String) {
-            try {
-                return new BigDecimal((String) val).longValue();
-            } catch (Exception e) {
-                return defaultValue;
-            }
-        }
-        return defaultValue;
+        return val.longValue();
     }
     
     /**
@@ -1472,14 +1411,11 @@ public Number optNumber(String key, Number defaultValue) {
             return (Number) val;
         }
         
-        if (val instanceof String) {
-            try {
-                return stringToNumber((String) val);
-            } catch (Exception e) {
-                return defaultValue;
-            }
+        try {
+            return stringToNumber(val.toString());
+        } catch (Exception e) {
+            return defaultValue;
         }
-        return defaultValue;
     }
     
     /**
@@ -2201,22 +2137,26 @@ protected static Number stringToNumber(final String val) throws NumberFormatExce
      * can't be converted, return the string.
      *
      * @param string
-     *            A String.
+     *            A String. can not be null.
      * @return A simple JSON value.
+     * @throws NullPointerException
+     *             Thrown if the string is null.
      */
     // Changes to this method must be copied to the corresponding method in
     // the XML class to keep full support for Android
     public static Object stringToValue(String string) {
-        if (string.equals("")) {
+        if ("".equals(string)) {
             return string;
         }
-        if (string.equalsIgnoreCase("true")) {
+
+        // check JSON key words true/false/null
+        if ("true".equalsIgnoreCase(string)) {
             return Boolean.TRUE;
         }
-        if (string.equalsIgnoreCase("false")) {
+        if ("false".equalsIgnoreCase(string)) {
             return Boolean.FALSE;
         }
-        if (string.equalsIgnoreCase("null")) {
+        if ("null".equalsIgnoreCase(string)) {
             return JSONObject.NULL;
         }
 
@@ -2228,7 +2168,8 @@ public static Object stringToValue(String string) {
         char initial = string.charAt(0);
         if ((initial >= '0' && initial <= '9') || initial == '-') {
             try {
-                // if we want full Big Number support this block can be replaced with:
+                // if we want full Big Number support the contents of this
+                // `try` block can be replaced with:
                 // return stringToNumber(string);
                 if (isDecimalNotation(string)) {
                     Double d = Double.valueOf(string);