Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSONObject.similar() numeric compare bug fix #617

Merged
merged 3 commits into from Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 22 additions & 6 deletions src/main/java/org/json/JSONObject.java
Expand Up @@ -1159,6 +1159,18 @@ public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
* 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;
}
Expand All @@ -1172,7 +1184,14 @@ static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) {
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){
Expand Down Expand Up @@ -1639,9 +1658,6 @@ private static <A extends Annotation> A getAnnotation(final Method m, final Clas
* 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
Expand Down Expand Up @@ -2135,8 +2151,8 @@ static boolean isNumberSimilar(Number l, Number r) {
// 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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/json/JSONPointer.java
Expand Up @@ -188,7 +188,7 @@ public JSONPointer(List<String> refTokens) {
}

/**
* @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", "~");
Expand Down Expand Up @@ -268,7 +268,7 @@ public String toString() {
* @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")
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/org/json/XMLParserConfiguration.java
Expand Up @@ -94,7 +94,7 @@ public XMLParserConfiguration (final boolean keepStrings) {
* 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.
Expand All @@ -109,7 +109,7 @@ public XMLParserConfiguration (final String cDataTagName) {
* 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.
Expand Down Expand Up @@ -182,7 +182,7 @@ protected XMLParserConfiguration clone() {
* 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;
Expand All @@ -193,7 +193,7 @@ public boolean isKeepStrings() {
* 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.
*/
Expand All @@ -208,7 +208,7 @@ public XMLParserConfiguration withKeepStrings(final boolean newVal) {
* 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;
Expand All @@ -220,7 +220,7 @@ public String getcDataTagName() {
* 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.
*/
Expand All @@ -235,7 +235,7 @@ public XMLParserConfiguration withcDataTagName(final String newVal) {
* 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;
Expand All @@ -247,7 +247,7 @@ public boolean isConvertNilAttributeToNull() {
* <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.
*/
Expand All @@ -262,7 +262,7 @@ public XMLParserConfiguration withConvertNilAttributeToNull(final boolean newVal
* 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;
Expand Down
5 changes: 4 additions & 1 deletion src/test/java/org/json/junit/JSONObjectTest.java
Expand Up @@ -126,6 +126,9 @@ public void verifySimilar() {

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
Expand Down Expand Up @@ -940,7 +943,7 @@ public void stringToValueNumbersTest() {
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 );
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/json/junit/JSONPointerTest.java
Expand Up @@ -120,7 +120,7 @@ public void tildeEscaping() {
/**
* 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() {
Expand All @@ -130,7 +130,7 @@ public void backslashHandling() {
/**
* 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() {
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/org/json/junit/data/ExceptionalBean.java
Expand Up @@ -8,7 +8,7 @@
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
*/
Expand Down