mirror of
https://github.com/stleary/JSON-java.git
synced 2026-01-24 00:03:17 -05:00
Compare commits
15 Commits
pre-releas
...
tech-debt-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b0d1942b4 | ||
|
|
197afddbfb | ||
|
|
1bdaacc8b0 | ||
|
|
c882783d58 | ||
|
|
5063d314a5 | ||
|
|
916fba5d39 | ||
|
|
aac376f305 | ||
|
|
32e56da786 | ||
|
|
50330430ce | ||
|
|
f1935f5254 | ||
|
|
e800cc349f | ||
|
|
72a1a48173 | ||
|
|
a381060f81 | ||
|
|
dadc3e59dc | ||
|
|
24fafcffeb |
14
build.gradle
14
build.gradle
@@ -3,9 +3,10 @@
|
|||||||
*/
|
*/
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
// apply plugin: 'jacoco'
|
apply plugin: 'jacoco'
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
|
// for now, publishing to maven is still a manual process
|
||||||
//plugins {
|
//plugins {
|
||||||
// id 'java'
|
// id 'java'
|
||||||
//id 'maven-publish'
|
//id 'maven-publish'
|
||||||
@@ -19,6 +20,17 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To view the report open build/reports/jacoco/test/html/index.html
|
||||||
|
jacocoTestReport {
|
||||||
|
reports {
|
||||||
|
html.required = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
finalizedBy jacocoTestReport
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testImplementation 'com.jayway.jsonpath:json-path:2.9.0'
|
testImplementation 'com.jayway.jsonpath:json-path:2.9.0'
|
||||||
|
|||||||
@@ -334,13 +334,11 @@ public class JSONArray implements Iterable<Object> {
|
|||||||
*/
|
*/
|
||||||
public boolean getBoolean(int index) throws JSONException {
|
public boolean getBoolean(int index) throws JSONException {
|
||||||
Object object = this.get(index);
|
Object object = this.get(index);
|
||||||
if (object.equals(Boolean.FALSE)
|
if (Boolean.FALSE.equals(object)
|
||||||
|| (object instanceof String && ((String) object)
|
|| (object instanceof String && "false".equalsIgnoreCase((String) object))) {
|
||||||
.equalsIgnoreCase("false"))) {
|
|
||||||
return false;
|
return false;
|
||||||
} else if (object.equals(Boolean.TRUE)
|
} else if (Boolean.TRUE.equals(object)
|
||||||
|| (object instanceof String && ((String) object)
|
|| (object instanceof String && "true".equalsIgnoreCase((String) object))) {
|
||||||
.equalsIgnoreCase("true"))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw wrongValueFormatException(index, "boolean", object, null);
|
throw wrongValueFormatException(index, "boolean", object, null);
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class JSONML {
|
|||||||
}
|
}
|
||||||
} else if (c == '[') {
|
} else if (c == '[') {
|
||||||
token = x.nextToken();
|
token = x.nextToken();
|
||||||
if (token.equals("CDATA") && x.next() == '[') {
|
if ("CDATA".equals(token) && x.next() == '[') {
|
||||||
if (ja != null) {
|
if (ja != null) {
|
||||||
ja.put(x.nextCDATA());
|
ja.put(x.nextCDATA());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -401,12 +401,17 @@ public class JSONObject {
|
|||||||
*/
|
*/
|
||||||
public JSONObject(Object bean) {
|
public JSONObject(Object bean) {
|
||||||
this();
|
this();
|
||||||
this.populateMap(bean);
|
this.populateMap(bean, new JSONParserConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject(Object bean, JSONParserConfiguration jsonParserConfiguration) {
|
||||||
|
this();
|
||||||
|
this.populateMap(bean, jsonParserConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONObject(Object bean, Set<Object> objectsRecord) {
|
private JSONObject(Object bean, Set<Object> objectsRecord) {
|
||||||
this();
|
this();
|
||||||
this.populateMap(bean, objectsRecord);
|
this.populateMap(bean, objectsRecord, new JSONParserConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -674,13 +679,11 @@ public class JSONObject {
|
|||||||
*/
|
*/
|
||||||
public boolean getBoolean(String key) throws JSONException {
|
public boolean getBoolean(String key) throws JSONException {
|
||||||
Object object = this.get(key);
|
Object object = this.get(key);
|
||||||
if (object.equals(Boolean.FALSE)
|
if (Boolean.FALSE.equals(object)
|
||||||
|| (object instanceof String && ((String) object)
|
|| (object instanceof String && "false".equalsIgnoreCase((String) object))) {
|
||||||
.equalsIgnoreCase("false"))) {
|
|
||||||
return false;
|
return false;
|
||||||
} else if (object.equals(Boolean.TRUE)
|
} else if (Boolean.TRUE.equals(object)
|
||||||
|| (object instanceof String && ((String) object)
|
|| (object instanceof String && "true".equalsIgnoreCase((String) object))) {
|
||||||
.equalsIgnoreCase("true"))) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw wrongValueFormatException(key, "Boolean", object, null);
|
throw wrongValueFormatException(key, "Boolean", object, null);
|
||||||
@@ -1764,11 +1767,11 @@ public class JSONObject {
|
|||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
* If a getter returned a non-finite number.
|
* If a getter returned a non-finite number.
|
||||||
*/
|
*/
|
||||||
private void populateMap(Object bean) {
|
private void populateMap(Object bean, JSONParserConfiguration jsonParserConfiguration) {
|
||||||
populateMap(bean, Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()));
|
populateMap(bean, Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()), jsonParserConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateMap(Object bean, Set<Object> objectsRecord) {
|
private void populateMap(Object bean, Set<Object> objectsRecord, JSONParserConfiguration jsonParserConfiguration) {
|
||||||
Class<?> klass = bean.getClass();
|
Class<?> klass = bean.getClass();
|
||||||
|
|
||||||
// If klass is a System class then set includeSuperClass to false.
|
// If klass is a System class then set includeSuperClass to false.
|
||||||
@@ -1777,18 +1780,12 @@ public class JSONObject {
|
|||||||
|
|
||||||
Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods();
|
Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods();
|
||||||
for (final Method method : methods) {
|
for (final Method method : methods) {
|
||||||
final int modifiers = method.getModifiers();
|
if (isValidMethod(method)) {
|
||||||
if (Modifier.isPublic(modifiers)
|
|
||||||
&& !Modifier.isStatic(modifiers)
|
|
||||||
&& method.getParameterTypes().length == 0
|
|
||||||
&& !method.isBridge()
|
|
||||||
&& method.getReturnType() != Void.TYPE
|
|
||||||
&& isValidMethodName(method.getName())) {
|
|
||||||
final String key = getKeyNameFromMethod(method);
|
final String key = getKeyNameFromMethod(method);
|
||||||
if (key != null && !key.isEmpty()) {
|
if (key != null && !key.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
final Object result = method.invoke(bean);
|
final Object result = method.invoke(bean);
|
||||||
if (result != null) {
|
if (result != null || jsonParserConfiguration.isUseNativeNulls()) {
|
||||||
// check cyclic dependency and throw error if needed
|
// check cyclic dependency and throw error if needed
|
||||||
// the wrap and populateMap combination method is
|
// the wrap and populateMap combination method is
|
||||||
// itself DFS recursive
|
// itself DFS recursive
|
||||||
@@ -1803,15 +1800,7 @@ public class JSONObject {
|
|||||||
|
|
||||||
objectsRecord.remove(result);
|
objectsRecord.remove(result);
|
||||||
|
|
||||||
// we don't use the result anywhere outside of wrap
|
closeClosable(result);
|
||||||
// if it's a resource we should be sure to close it
|
|
||||||
// after calling toString
|
|
||||||
if (result instanceof Closeable) {
|
|
||||||
try {
|
|
||||||
((Closeable) result).close();
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException ignore) {
|
} catch (IllegalAccessException ignore) {
|
||||||
} catch (IllegalArgumentException ignore) {
|
} catch (IllegalArgumentException ignore) {
|
||||||
@@ -1837,7 +1826,7 @@ public class JSONObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSONPropertyName annotation = getAnnotation(method, JSONPropertyName.class);
|
JSONPropertyName annotation = getAnnotation(method, JSONPropertyName.class);
|
||||||
if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) {
|
if (annotationValueNotEmpty(annotation)) {
|
||||||
return annotation.value();
|
return annotation.value();
|
||||||
}
|
}
|
||||||
String key;
|
String key;
|
||||||
@@ -1863,6 +1852,46 @@ public class JSONObject {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks if the annotation is not null and the {@link JSONPropertyName#value()} is not null and is not empty.
|
||||||
|
* @param annotation the annotation to check
|
||||||
|
* @return true if the annotation and the value is not null and not empty, false otherwise.
|
||||||
|
*/
|
||||||
|
private static boolean annotationValueNotEmpty(JSONPropertyName annotation) {
|
||||||
|
return annotation != null && annotation.value() != null && !annotation.value().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the method is valid for the {@link #populateMap(Object, Set, JSONParserConfiguration)} use case
|
||||||
|
* @param method the Method to check
|
||||||
|
* @return true, if valid, false otherwise.
|
||||||
|
*/
|
||||||
|
private static boolean isValidMethod(Method method) {
|
||||||
|
final int modifiers = method.getModifiers();
|
||||||
|
return Modifier.isPublic(modifiers)
|
||||||
|
&& !Modifier.isStatic(modifiers)
|
||||||
|
&& method.getParameterTypes().length == 0
|
||||||
|
&& !method.isBridge()
|
||||||
|
&& method.getReturnType() != Void.TYPE
|
||||||
|
&& isValidMethodName(method.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calls {@link Closeable#close()} on the input, if it is an instance of Closable.
|
||||||
|
* @param input the input to close, if possible.
|
||||||
|
*/
|
||||||
|
private static void closeClosable(Object input) {
|
||||||
|
// we don't use the result anywhere outside of wrap
|
||||||
|
// if it's a resource we should be sure to close it
|
||||||
|
// after calling toString
|
||||||
|
if (input instanceof Closeable) {
|
||||||
|
try {
|
||||||
|
((Closeable) input).close();
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches the class hierarchy to see if the method or it's super
|
* Searches the class hierarchy to see if the method or it's super
|
||||||
* implementations and interfaces has the annotation.
|
* implementations and interfaces has the annotation.
|
||||||
@@ -1906,7 +1935,7 @@ public class JSONObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If the superclass is Object, no annotations will be found any more
|
//If the superclass is Object, no annotations will be found any more
|
||||||
if (c.getSuperclass().equals(Object.class))
|
if (Object.class.equals(c.getSuperclass()))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1964,7 +1993,7 @@ public class JSONObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If the superclass is Object, no annotations will be found any more
|
//If the superclass is Object, no annotations will be found any more
|
||||||
if (c.getSuperclass().equals(Object.class))
|
if (Object.class.equals(c.getSuperclass()))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2750,13 +2779,13 @@ public class JSONObject {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (object instanceof JSONObject || object instanceof JSONArray
|
if (object instanceof JSONObject || object instanceof JSONArray
|
||||||
|| NULL.equals(object) || object instanceof JSONString
|
|| object instanceof JSONString || object instanceof String
|
||||||
|| object instanceof Byte || object instanceof Character
|
|| object instanceof Byte || object instanceof Character
|
||||||
|| object instanceof Short || object instanceof Integer
|
|| object instanceof Short || object instanceof Integer
|
||||||
|| object instanceof Long || object instanceof Boolean
|
|| object instanceof Long || object instanceof Boolean
|
||||||
|| object instanceof Float || object instanceof Double
|
|| object instanceof Float || object instanceof Double
|
||||||
|| object instanceof String || object instanceof BigInteger
|
|| object instanceof BigInteger || object instanceof BigDecimal
|
||||||
|| object instanceof BigDecimal || object instanceof Enum) {
|
|| object instanceof Enum) {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3010,24 +3039,4 @@ public class JSONObject {
|
|||||||
"JavaBean object contains recursively defined member variable of key " + quote(key)
|
"JavaBean object contains recursively defined member variable of key " + quote(key)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For a prospective number, remove the leading zeros
|
|
||||||
* @param value prospective number
|
|
||||||
* @return number without leading zeros
|
|
||||||
*/
|
|
||||||
private static String removeLeadingZerosOfNumber(String value){
|
|
||||||
if (value.equals("-")){return value;}
|
|
||||||
boolean negativeFirstChar = (value.charAt(0) == '-');
|
|
||||||
int counter = negativeFirstChar ? 1:0;
|
|
||||||
while (counter < value.length()){
|
|
||||||
if (value.charAt(counter) != '0'){
|
|
||||||
if (negativeFirstChar) {return "-".concat(value.substring(counter));}
|
|
||||||
return value.substring(counter);
|
|
||||||
}
|
|
||||||
++counter;
|
|
||||||
}
|
|
||||||
if (negativeFirstChar) {return "-0";}
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public class JSONPointer {
|
|||||||
if (pointer == null) {
|
if (pointer == null) {
|
||||||
throw new NullPointerException("pointer cannot be null");
|
throw new NullPointerException("pointer cannot be null");
|
||||||
}
|
}
|
||||||
if (pointer.isEmpty() || pointer.equals("#")) {
|
if (pointer.isEmpty() || "#".equals(pointer)) {
|
||||||
this.refTokens = Collections.emptyList();
|
this.refTokens = Collections.emptyList();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,7 @@ public class JSONPointer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder rval = new StringBuilder("");
|
StringBuilder rval = new StringBuilder();
|
||||||
for (String token: this.refTokens) {
|
for (String token: this.refTokens) {
|
||||||
rval.append('/').append(escape(token));
|
rval.append('/').append(escape(token));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4011,5 +4011,37 @@ public class JSONObjectTest {
|
|||||||
nestedMap.put("t", buildNestedMap(maxDepth - 1));
|
nestedMap.put("t", buildNestedMap(maxDepth - 1));
|
||||||
return nestedMap;
|
return nestedMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the behavior of the {@link JSONObject} when parsing a bean with null fields
|
||||||
|
* using a custom {@link JSONParserConfiguration} that enables the use of native nulls.
|
||||||
|
*
|
||||||
|
* <p>This test ensures that uninitialized fields in the bean are serialized correctly
|
||||||
|
* into the resulting JSON object, and their keys are present in the JSON string output.</p>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void jsonObjectParseNullFieldsWithParserConfiguration() {
|
||||||
|
JSONParserConfiguration jsonParserConfiguration = new JSONParserConfiguration();
|
||||||
|
RecursiveBean bean = new RecursiveBean(null);
|
||||||
|
JSONObject jsonObject = new JSONObject(bean, jsonParserConfiguration.withUseNativeNulls(true));
|
||||||
|
assertTrue("name key should be present", jsonObject.has("name"));
|
||||||
|
assertTrue("ref key should be present", jsonObject.has("ref"));
|
||||||
|
assertTrue("ref2 key should be present", jsonObject.has("ref2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the behavior of the {@link JSONObject} when parsing a bean with null fields
|
||||||
|
* without using a custom {@link JSONParserConfiguration}.
|
||||||
|
*
|
||||||
|
* <p>This test ensures that uninitialized fields in the bean are not serialized
|
||||||
|
* into the resulting JSON object, and the object remains empty.</p>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void jsonObjectParseNullFieldsWithoutParserConfiguration() {
|
||||||
|
RecursiveBean bean = new RecursiveBean(null);
|
||||||
|
JSONObject jsonObject = new JSONObject(bean);
|
||||||
|
assertTrue("JSONObject should be empty", jsonObject.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user