Merge pull request #617 from johnjaylward/issue-616-similar-bug

Fixes #616 similar() problem comparing double vs BigDecimal
This commit is contained in:
Sean Leary 2021-07-26 17:47:03 -05:00 committed by GitHub
commit bb048e3ffb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 40 additions and 21 deletions

View File

@ -1159,6 +1159,18 @@ public class JSONObject {
* to convert.
*/
static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
return objectToBigDecimal(val, defaultValue, true);
}
/**
* @param val value to convert
* @param defaultValue default value to return is the conversion doesn't work or is null.
* @param exact When <code>true</code>, then {@link Double} and {@link Float} values will be converted exactly.
* When <code>false</code>, they will be converted to {@link String} values before converting to {@link BigDecimal}.
* @return BigDecimal conversion of the original value, or the defaultValue if unable
* to convert.
*/
static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue, boolean exact) {
if (NULL.equals(val)) {
return defaultValue;
}
@ -1172,7 +1184,14 @@ public class JSONObject {
if (!numberIsFinite((Number)val)) {
return defaultValue;
}
return new BigDecimal(((Number) val).doubleValue());
if (exact) {
return new BigDecimal(((Number)val).doubleValue());
}else {
// use the string constructor so that we maintain "nice" values for doubles and floats
// the double constructor will translate doubles to "exact" values instead of the likely
// intended representation
return new BigDecimal(val.toString());
}
}
if (val instanceof Long || val instanceof Integer
|| val instanceof Short || val instanceof Byte){
@ -1639,9 +1658,6 @@ public class JSONObject {
* implementations and interfaces has the annotation. Returns the depth of the
* annotation in the hierarchy.
*
* @param <A>
* type of the annotation
*
* @param m
* method to check
* @param annotationClass
@ -2135,8 +2151,8 @@ public class JSONObject {
// 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);
final BigDecimal lBigDecimal = objectToBigDecimal(l, null, false);
final BigDecimal rBigDecimal = objectToBigDecimal(r, null, false);
if (lBigDecimal == null || rBigDecimal == null) {
return false;
}

View File

@ -188,7 +188,7 @@ public class JSONPointer {
}
/**
* @see https://tools.ietf.org/html/rfc6901#section-3
* @see <a href="https://tools.ietf.org/html/rfc6901#section-3">rfc6901 section 3</a>
*/
private static String unescape(String token) {
return token.replace("~1", "/").replace("~0", "~");
@ -268,7 +268,7 @@ public class JSONPointer {
* @param token the JSONPointer segment value to be escaped
* @return the escaped value for the token
*
* @see https://tools.ietf.org/html/rfc6901#section-3
* @see <a href="https://tools.ietf.org/html/rfc6901#section-3">rfc6901 section 3</a>
*/
private static String escape(String token) {
return token.replace("~", "~0")

View File

@ -94,7 +94,7 @@ public class XMLParserConfiguration {
* Configure the parser string processing to try and convert XML values to JSON values and
* use the passed CDATA Tag Name the processing value. Pass <code>null</code> to
* disable CDATA processing
* @param cDataTagName<code>null</code> to disable CDATA processing. Any other value
* @param cDataTagName <code>null</code> to disable CDATA processing. Any other value
* to use that value as the JSONObject key name to process as CDATA.
* @deprecated This constructor has been deprecated in favor of using the new builder
* pattern for the configuration.
@ -109,7 +109,7 @@ public class XMLParserConfiguration {
* Configure the parser to use custom settings.
* @param keepStrings <code>true</code> to parse all values as string.
* <code>false</code> to try and convert XML string values into a JSON value.
* @param cDataTagName<code>null</code> to disable CDATA processing. Any other value
* @param cDataTagName <code>null</code> to disable CDATA processing. Any other value
* to use that value as the JSONObject key name to process as CDATA.
* @deprecated This constructor has been deprecated in favor of using the new builder
* pattern for the configuration.
@ -182,7 +182,7 @@ public class XMLParserConfiguration {
* When parsing the XML into JSON, specifies if values should be kept as strings (<code>true</code>), or if
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @return The {@link #keepStrings} configuration value.
* @return The <code>keepStrings</code> configuration value.
*/
public boolean isKeepStrings() {
return this.keepStrings;
@ -193,7 +193,7 @@ public class XMLParserConfiguration {
* they should try to be guessed into JSON values (numeric, boolean, string)
*
* @param newVal
* new value to use for the {@link #keepStrings} configuration option.
* new value to use for the <code>keepStrings</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@ -208,7 +208,7 @@ public class XMLParserConfiguration {
* been the value "content" but can be changed. Use <code>null</code> to indicate no CDATA
* processing.
*
* @return The {@link #cDataTagName} configuration value.
* @return The <code>cDataTagName</code> configuration value.
*/
public String getcDataTagName() {
return this.cDataTagName;
@ -220,7 +220,7 @@ public class XMLParserConfiguration {
* processing.
*
* @param newVal
* new value to use for the {@link #cDataTagName} configuration option.
* new value to use for the <code>cDataTagName</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@ -235,7 +235,7 @@ public class XMLParserConfiguration {
* should be kept as attribute(<code>false</code>), or they should be converted to
* <code>null</code>(<code>true</code>)
*
* @return The {@link #convertNilAttributeToNull} configuration value.
* @return The <code>convertNilAttributeToNull</code> configuration value.
*/
public boolean isConvertNilAttributeToNull() {
return this.convertNilAttributeToNull;
@ -247,7 +247,7 @@ public class XMLParserConfiguration {
* <code>null</code>(<code>true</code>)
*
* @param newVal
* new value to use for the {@link #convertNilAttributeToNull} configuration option.
* new value to use for the <code>convertNilAttributeToNull</code> configuration option.
*
* @return The existing configuration will not be modified. A new configuration is returned.
*/
@ -262,7 +262,7 @@ public class XMLParserConfiguration {
* will be converted to target type defined to client in this configuration
* {@code Map<String, XMLXsiTypeConverter<?>>} to parse values with attribute
* xsi:type="integer" as integer, xsi:type="string" as string
* @return {@link #xsiTypeMap} unmodifiable configuration map.
* @return <code>xsiTypeMap</code> unmodifiable configuration map.
*/
public Map<String, XMLXsiTypeConverter<?>> getXsiTypeMap() {
return this.xsiTypeMap;

View File

@ -126,6 +126,9 @@ public class JSONObjectTest {
assertTrue("Should eval to true", obj1.similar(obj4));
// verify that a double and big decimal are "similar"
assertTrue("should eval to true",new JSONObject().put("a",1.1d).similar(new JSONObject("{\"a\":1.1}")));
}
@Test
@ -940,7 +943,7 @@ public class JSONObjectTest {
assertTrue("-0 Should be a Double!",JSONObject.stringToValue("-0") instanceof Double);
assertTrue("-0.0 Should be a Double!",JSONObject.stringToValue("-0.0") instanceof Double);
assertTrue("'-' Should be a String!",JSONObject.stringToValue("-") instanceof String);
assertTrue( "0.2 should be a Double!",
assertTrue( "0.2 should be a BigDecimal!",
JSONObject.stringToValue( "0.2" ) instanceof BigDecimal );
assertTrue( "Doubles should be BigDecimal, even when incorrectly converting floats!",
JSONObject.stringToValue( new Double( "0.2f" ).toString() ) instanceof BigDecimal );

View File

@ -120,7 +120,7 @@ public class JSONPointerTest {
/**
* We pass backslashes as-is
*
* @see https://tools.ietf.org/html/rfc6901#section-3
* @see <a href="https://tools.ietf.org/html/rfc6901#section-3">rfc6901 section 3</a>
*/
@Test
public void backslashHandling() {
@ -130,7 +130,7 @@ public class JSONPointerTest {
/**
* We pass quotations as-is
*
* @see https://tools.ietf.org/html/rfc6901#section-3
* @see <a href="https://tools.ietf.org/html/rfc6901#section-3">rfc6901 section 3</a>
*/
@Test
public void quotationHandling() {

View File

@ -8,7 +8,7 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
/**
* Object for testing the exception handling in {@link JSONObject#populateMap}.
* Object for testing the exception handling in {@link org.json.JSONObject#populateMap}.
*
* @author John Aylward
*/