42 Commits

Author SHA1 Message Date
Sean Leary
37f5bf28e9 Update README.md 2018-08-13 20:46:20 -05:00
Sean Leary
7a17ae0b3e Merge pull request #421 from strkkk/add-emptiness-methods
add isEmpty and isNotEmpty methods
2018-05-26 09:56:23 -05:00
Andrei Paikin
7cad4c3b26 partially revert changes 2018-05-25 22:17:03 +03:00
Andrei_Paikin
05074386d3 change length comparison to isEmpty method 2018-05-21 16:58:13 +03:00
Andrei Paikin
a490ebdb78 add isEmpty and isNotEmpty methods 2018-05-19 09:42:21 +03:00
Sean Leary
3c1535d724 Merge pull request #417 from stleary/fix-double-ctor
fix double ctor in JSONWriter
2018-05-16 08:46:01 -05:00
stleary
a6284df9c7 initial commit 2018-05-02 20:28:45 -05:00
Sean Leary
bfb300835f Merge pull request #411 from johnjaylward/JSONPointerTrailingSlash
Fix for invalid processing of trailing / for JSON Pointer
2018-03-27 07:44:48 -05:00
Sean Leary
06e9ad280f Merge pull request #403 from paulpolushkin/idea-fix
ignore Intellij Idea project files
2018-03-19 19:10:29 -05:00
John J. Aylward
2362c930d1 Fixes #410. Invalid processing of trailing / for JSON Pointer 2018-03-19 09:57:06 -04:00
Sean Leary
2a6b5bacc5 Merge pull request #406 from johnjaylward/FixBeanKeyNameing
Adds annotations to customize field names during Bean serialization
2018-03-14 21:18:23 -05:00
John J. Aylward
a509a28ed4 Cleans up the name check a little to be more permissive on what can be tagged with the new JSONPropertyName annotation.
Also updates the javadoc to reflect the new name allowances
2018-03-11 17:22:05 -04:00
Sean Leary
1c1ef5b211 Merge pull request #405 from johnjaylward/FixNPE
Updates javadoc to match actual exceptions thrown.
2018-03-11 15:57:23 -05:00
John J. Aylward
74b9a60f98 Adds annotation to support custom field names during Bean serialization 2018-03-11 16:28:24 -04:00
John J. Aylward
b63b976acb Updates javadoc to match actual exceptions thrown.
Also optimizes some boxing statements and returns.
2018-03-07 12:35:56 -05:00
Pavel Polushkin
97e180444d ignore Intellij Idea project files 2018-02-25 13:08:58 +03:00
Sean Leary
d402a99fd8 Merge pull request #400 from foldvari/master
XML toJSONObject(Reader reader)
2018-02-10 11:09:26 -06:00
György Földvári
7073bc8c47 XML toJSONObject(Readre reader) 2018-02-04 18:43:35 +01:00
Sean Leary
61cdfefc36 Update README.md 2018-01-30 19:46:24 -06:00
Sean Leary
fbad2d0017 Merge pull request #392 from philippgille/patch-1
Remove wrong apostrophe
2018-01-20 03:39:07 -06:00
Philipp Gillé
15719886f7 Remove wrong apostrophe 2018-01-17 18:41:48 +01:00
Sean Leary
ca45b02ffc Merge pull request #381 from fleipold/maven-badge
Adding maven badge to readme
2018-01-14 10:18:46 -06:00
Felix Leipold
b6efbabc32 Making links markdown links 2017-11-17 12:47:49 +01:00
Felix Leipold
9eb8c27724 Marking up class and method names as inline code 2017-11-17 12:33:50 +01:00
Felix Leipold
195963357c Make file names bold 2017-11-17 12:24:17 +01:00
Felix Leipold
28efdb4860 Moving Badge below title 2017-11-17 12:24:03 +01:00
Felix Leipold
c88653ca2e History with fixed font 2017-11-14 17:23:53 +01:00
Felix Leipold
b3068d9fe4 Marking file and class names with single quotes 2017-11-14 17:23:52 +01:00
Felix Leipold
dba4afd0cf Adding maven repo badge 2017-11-14 17:23:36 +01:00
Felix Leipold
26160e1619 Remove trailing whitespace 2017-11-14 17:23:36 +01:00
Felix Leipold
b7e2eee4d6 Renaming README to README.md 2017-11-14 17:23:36 +01:00
Sean Leary
28e09dc493 Merge pull request #380 from johnjaylward/FixFalsePositiveSimilar
Fix for false positives in similar functions
2017-11-11 16:12:06 -06:00
John J. Aylward
4a4b2db8c1 fix for issue #379 2017-11-06 10:28:28 -05:00
Sean Leary
f16682bf44 Merge pull request #375 from johnjaylward/FixExceptionWrapping
fixes wrapped exceptions
2017-11-02 21:42:29 -05:00
John J. Aylward
18952b5ac0 fixes wrapped exceptions 2017-11-02 22:32:24 -04:00
Sean Leary
d2a66a4287 Merge pull request #373 from johnjaylward/UnclosedJSONArray
Fixes Unclosed json array stack overflow
2017-11-02 20:02:26 -05:00
Sean Leary
722003d479 Merge pull request #350 from johnjaylward/AndroidSupport
Updates for supporting the Android API
2017-10-30 11:33:10 -05:00
John J. Aylward
ed8745cd63 fixes #372.
Corrects behavior of unclosed arrays
2017-10-30 08:18:59 -04:00
John J. Aylward
057e0c75ca Merge remote-tracking branch 'origin/master' into AndroidSupport 2017-10-27 13:28:20 -04:00
John J. Aylward
3997a90d58 update constructor call to match Android implementation 2017-07-07 12:24:27 -04:00
John J. Aylward
1736a60ffe adds comment for the API change 2017-07-07 12:17:45 -04:00
John J. Aylward
e8b1b66888 Updates for supporting the Android API 2017-07-07 12:17:39 -04:00
15 changed files with 893 additions and 338 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
# ignore eclipse project files
.project
.classpath
# ignore Intellij Idea project files
.idea
*.iml

View File

@@ -1,7 +1,5 @@
package org.json; package org.json;
import java.util.Map.Entry;
/* /*
Copyright (c) 2002 JSON.org Copyright (c) 2002 JSON.org
@@ -24,7 +22,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
/** /**
* Convert a web browser cookie list string to a JSONObject and back. * Convert a web browser cookie list string to a JSONObject and back.
@@ -69,17 +67,17 @@ public class CookieList {
*/ */
public static String toString(JSONObject jo) throws JSONException { public static String toString(JSONObject jo) throws JSONException {
boolean b = false; boolean b = false;
StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
for (final Entry<String,?> entry : jo.entrySet()) { // Don't use the new entrySet API to maintain Android support
final String key = entry.getKey(); for (final String key : jo.keySet()) {
final Object value = entry.getValue(); final Object value = jo.opt(key);
if (!JSONObject.NULL.equals(value)) { if (!JSONObject.NULL.equals(value)) {
if (b) { if (b) {
sb.append(';'); sb.append(';');
} }
sb.append(Cookie.escape(key)); sb.append(Cookie.escape(key));
sb.append("="); sb.append("=");
sb.append(Cookie.escape(value.toString())); sb.append(Cookie.escape(value.toString()));
b = true; b = true;
} }
} }

View File

@@ -25,7 +25,6 @@ SOFTWARE.
*/ */
import java.util.Locale; import java.util.Locale;
import java.util.Map.Entry;
/** /**
* Convert an HTTP header to a JSONObject and back. * Convert an HTTP header to a JSONObject and back.
@@ -145,11 +144,12 @@ public class HTTP {
throw new JSONException("Not enough material for an HTTP header."); throw new JSONException("Not enough material for an HTTP header.");
} }
sb.append(CRLF); sb.append(CRLF);
for (final Entry<String,?> entry : jo.entrySet()) { // Don't use the new entrySet API to maintain Android support
final String key = entry.getKey(); for (final String key : jo.keySet()) {
String value = jo.optString(key);
if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) && if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) &&
!"Reason-Phrase".equals(key) && !"Method".equals(key) && !"Reason-Phrase".equals(key) && !"Method".equals(key) &&
!"Request-URI".equals(key) && !JSONObject.NULL.equals(entry.getValue())) { !"Request-URI".equals(key) && !JSONObject.NULL.equals(value)) {
sb.append(key); sb.append(key);
sb.append(": "); sb.append(": ");
sb.append(jo.optString(key)); sb.append(jo.optString(key));

View File

@@ -36,6 +36,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* A JSONArray is an ordered sequence of values. Its external text form is a * A JSONArray is an ordered sequence of values. Its external text form is a
* string wrapped in square brackets with commas separating the values. The * string wrapped in square brackets with commas separating the values. The
@@ -107,7 +108,13 @@ public class JSONArray implements Iterable<Object> {
if (x.nextClean() != '[') { if (x.nextClean() != '[') {
throw x.syntaxError("A JSONArray text must start with '['"); throw x.syntaxError("A JSONArray text must start with '['");
} }
if (x.nextClean() != ']') {
char nextChar = x.nextClean();
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
}
if (nextChar != ']') {
x.back(); x.back();
for (;;) { for (;;) {
if (x.nextClean() == ',') { if (x.nextClean() == ',') {
@@ -118,8 +125,16 @@ public class JSONArray implements Iterable<Object> {
this.myArrayList.add(x.nextValue()); this.myArrayList.add(x.nextValue());
} }
switch (x.nextClean()) { switch (x.nextClean()) {
case 0:
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
case ',': case ',':
if (x.nextClean() == ']') { nextChar = x.nextClean();
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
}
if (nextChar == ']') {
return; return;
} }
x.back(); x.back();
@@ -168,7 +183,7 @@ public class JSONArray implements Iterable<Object> {
* Construct a JSONArray from an array * Construct a JSONArray from an array
* *
* @throws JSONException * @throws JSONException
* If not an array. * If not an array or if an array value is non-finite number.
*/ */
public JSONArray(Object array) throws JSONException { public JSONArray(Object array) throws JSONException {
this(); this();
@@ -451,11 +466,11 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Determine if the value is null. * Determine if the value is <code>null</code>.
* *
* @param index * @param index
* The index must be between 0 and length() - 1. * The index must be between 0 and length() - 1.
* @return true if the value at the index is null, or if there is no value. * @return true if the value at the index is <code>null</code>, or if there is no value.
*/ */
public boolean isNull(int index) { public boolean isNull(int index) {
return JSONObject.NULL.equals(this.opt(index)); return JSONObject.NULL.equals(this.opt(index));
@@ -939,8 +954,7 @@ public class JSONArray implements Iterable<Object> {
* @return this. * @return this.
*/ */
public JSONArray put(boolean value) { public JSONArray put(boolean value) {
this.put(value ? Boolean.TRUE : Boolean.FALSE); return this.put(value ? Boolean.TRUE : Boolean.FALSE);
return this;
} }
/** /**
@@ -950,10 +964,11 @@ public class JSONArray implements Iterable<Object> {
* @param value * @param value
* A Collection value. * A Collection value.
* @return this. * @return this.
* @throws JSONException
* If the value is non-finite number.
*/ */
public JSONArray put(Collection<?> value) { public JSONArray put(Collection<?> value) {
this.put(new JSONArray(value)); return this.put(new JSONArray(value));
return this;
} }
/** /**
@@ -961,15 +976,25 @@ public class JSONArray implements Iterable<Object> {
* *
* @param value * @param value
* A double value. * A double value.
* @return this.
* @throws JSONException * @throws JSONException
* if the value is not finite. * if the value is not finite.
* @return this.
*/ */
public JSONArray put(double value) throws JSONException { public JSONArray put(double value) throws JSONException {
Double d = new Double(value); return this.put(Double.valueOf(value));
JSONObject.testValidity(d); }
this.put(d);
return this; /**
* Append a float value. This increases the array's length by one.
*
* @param value
* A float value.
* @return this.
* @throws JSONException
* if the value is not finite.
*/
public JSONArray put(float value) throws JSONException {
return this.put(Float.valueOf(value));
} }
/** /**
@@ -980,8 +1005,7 @@ public class JSONArray implements Iterable<Object> {
* @return this. * @return this.
*/ */
public JSONArray put(int value) { public JSONArray put(int value) {
this.put(new Integer(value)); return this.put(Integer.valueOf(value));
return this;
} }
/** /**
@@ -992,8 +1016,7 @@ public class JSONArray implements Iterable<Object> {
* @return this. * @return this.
*/ */
public JSONArray put(long value) { public JSONArray put(long value) {
this.put(new Long(value)); return this.put(Long.valueOf(value));
return this;
} }
/** /**
@@ -1003,10 +1026,13 @@ public class JSONArray implements Iterable<Object> {
* @param value * @param value
* A Map value. * A Map value.
* @return this. * @return this.
* @throws JSONException
* If a value in the map is non-finite number.
* @throws NullPointerException
* If a key in the map is <code>null</code>
*/ */
public JSONArray put(Map<?, ?> value) { public JSONArray put(Map<?, ?> value) {
this.put(new JSONObject(value)); return this.put(new JSONObject(value));
return this;
} }
/** /**
@@ -1017,8 +1043,11 @@ public class JSONArray implements Iterable<Object> {
* Integer, JSONArray, JSONObject, Long, or String, or the * Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object. * JSONObject.NULL object.
* @return this. * @return this.
* @throws JSONException
* If the value is non-finite number.
*/ */
public JSONArray put(Object value) { public JSONArray put(Object value) {
JSONObject.testValidity(value);
this.myArrayList.add(value); this.myArrayList.add(value);
return this; return this;
} }
@@ -1037,8 +1066,7 @@ public class JSONArray implements Iterable<Object> {
* If the index is negative. * If the index is negative.
*/ */
public JSONArray put(int index, boolean value) throws JSONException { public JSONArray put(int index, boolean value) throws JSONException {
this.put(index, value ? Boolean.TRUE : Boolean.FALSE); return this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
return this;
} }
/** /**
@@ -1051,11 +1079,10 @@ public class JSONArray implements Iterable<Object> {
* A Collection value. * A Collection value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the index is negative or if the value is not finite. * If the index is negative or if the value is non-finite.
*/ */
public JSONArray put(int index, Collection<?> value) throws JSONException { public JSONArray put(int index, Collection<?> value) throws JSONException {
this.put(index, new JSONArray(value)); return this.put(index, new JSONArray(value));
return this;
} }
/** /**
@@ -1069,11 +1096,27 @@ public class JSONArray implements Iterable<Object> {
* A double value. * A double value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the index is negative or if the value is not finite. * If the index is negative or if the value is non-finite.
*/ */
public JSONArray put(int index, double value) throws JSONException { public JSONArray put(int index, double value) throws JSONException {
this.put(index, new Double(value)); return this.put(index, Double.valueOf(value));
return this; }
/**
* Put or replace a float value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index
* The subscript.
* @param value
* A float value.
* @return this.
* @throws JSONException
* If the index is negative or if the value is non-finite.
*/
public JSONArray put(int index, float value) throws JSONException {
return this.put(index, Float.valueOf(value));
} }
/** /**
@@ -1090,8 +1133,7 @@ public class JSONArray implements Iterable<Object> {
* If the index is negative. * If the index is negative.
*/ */
public JSONArray put(int index, int value) throws JSONException { public JSONArray put(int index, int value) throws JSONException {
this.put(index, new Integer(value)); return this.put(index, Integer.valueOf(value));
return this;
} }
/** /**
@@ -1108,8 +1150,7 @@ public class JSONArray implements Iterable<Object> {
* If the index is negative. * If the index is negative.
*/ */
public JSONArray put(int index, long value) throws JSONException { public JSONArray put(int index, long value) throws JSONException {
this.put(index, new Long(value)); return this.put(index, Long.valueOf(value));
return this;
} }
/** /**
@@ -1124,6 +1165,8 @@ public class JSONArray implements Iterable<Object> {
* @throws JSONException * @throws JSONException
* If the index is negative or if the the value is an invalid * If the index is negative or if the the value is an invalid
* number. * number.
* @throws NullPointerException
* If a key in the map is <code>null</code>
*/ */
public JSONArray put(int index, Map<?, ?> value) throws JSONException { public JSONArray put(int index, Map<?, ?> value) throws JSONException {
this.put(index, new JSONObject(value)); this.put(index, new JSONObject(value));
@@ -1147,25 +1190,26 @@ public class JSONArray implements Iterable<Object> {
* number. * number.
*/ */
public JSONArray put(int index, Object value) throws JSONException { public JSONArray put(int index, Object value) throws JSONException {
JSONObject.testValidity(value);
if (index < 0) { if (index < 0) {
throw new JSONException("JSONArray[" + index + "] not found."); throw new JSONException("JSONArray[" + index + "] not found.");
} }
if (index < this.length()) { if (index < this.length()) {
JSONObject.testValidity(value);
this.myArrayList.set(index, value); this.myArrayList.set(index, value);
} else if(index == this.length()){ return this;
// simple append
this.put(value);
} else {
// if we are inserting past the length, we want to grow the array all at once
// instead of incrementally.
this.myArrayList.ensureCapacity(index + 1);
while (index != this.length()) {
this.put(JSONObject.NULL);
}
this.put(value);
} }
return this; if(index == this.length()){
// simple append
return this.put(value);
}
// if we are inserting past the length, we want to grow the array all at once
// instead of incrementally.
this.myArrayList.ensureCapacity(index + 1);
while (index != this.length()) {
// we don't need to test validity of NULL objects
this.myArrayList.add(JSONObject.NULL);
}
return this.put(value);
} }
/** /**
@@ -1275,7 +1319,7 @@ public class JSONArray implements Iterable<Object> {
Object valueThis = this.myArrayList.get(i); Object valueThis = this.myArrayList.get(i);
Object valueOther = ((JSONArray)other).myArrayList.get(i); Object valueOther = ((JSONArray)other).myArrayList.get(i);
if(valueThis == valueOther) { if(valueThis == valueOther) {
return true; continue;
} }
if(valueThis == null) { if(valueThis == null) {
return false; return false;
@@ -1308,7 +1352,7 @@ public class JSONArray implements Iterable<Object> {
* If any of the names are null. * If any of the names are null.
*/ */
public JSONObject toJSONObject(JSONArray names) throws JSONException { public JSONObject toJSONObject(JSONArray names) throws JSONException {
if (names == null || names.length() == 0 || this.length() == 0) { if (names == null || names.isEmpty() || this.isEmpty()) {
return null; return null;
} }
JSONObject jo = new JSONObject(names.length()); JSONObject jo = new JSONObject(names.length());
@@ -1484,4 +1528,14 @@ public class JSONArray implements Iterable<Object> {
} }
return results; return results;
} }
/**
* Check if JSONArray is empty.
*
* @return true if JSONArray is empty, otherwise false.
*/
public boolean isEmpty() {
return myArrayList.isEmpty();
}
} }

View File

@@ -1,7 +1,5 @@
package org.json; package org.json;
import java.util.Map.Entry;
/* /*
Copyright (c) 2008 JSON.org Copyright (c) 2008 JSON.org
@@ -416,10 +414,10 @@ public class JSONML {
// Emit the attributes // Emit the attributes
for (final Entry<String, ?> entry : jo.entrySet()) { // Don't use the new entrySet API to maintain Android support
final String key = entry.getKey(); for (final String key : jo.keySet()) {
final Object value = jo.opt(key);
XML.noSpace(key); XML.noSpace(key);
final Object value = entry.getValue();
if (value != null) { if (value != null) {
sb.append(' '); sb.append(' ');
sb.append(XML.escape(key)); sb.append(XML.escape(key));
@@ -495,11 +493,11 @@ public class JSONML {
//Emit the attributes //Emit the attributes
for (final Entry<String, ?> entry : jo.entrySet()) { // Don't use the new entrySet API to maintain Android support
final String key = entry.getKey(); for (final String key : jo.keySet()) {
if (!"tagName".equals(key) && !"childNodes".equals(key)) { if (!"tagName".equals(key) && !"childNodes".equals(key)) {
XML.noSpace(key); XML.noSpace(key);
value = entry.getValue(); value = jo.opt(key);
if (value != null) { if (value != null) {
sb.append(' '); sb.append(' ');
sb.append(XML.escape(key)); sb.append(XML.escape(key));

View File

@@ -29,6 +29,7 @@ import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@@ -271,6 +272,10 @@ public class JSONObject {
* @param m * @param m
* A map object that can be used to initialize the contents of * A map object that can be used to initialize the contents of
* the JSONObject. * the JSONObject.
* @throws JSONException
* If a value in the map is non-finite number.
* @throws NullPointerException
* If a key in the map is <code>null</code>
*/ */
public JSONObject(Map<?, ?> m) { public JSONObject(Map<?, ?> m) {
if (m == null) { if (m == null) {
@@ -278,6 +283,9 @@ public class JSONObject {
} else { } else {
this.map = new HashMap<String, Object>(m.size()); this.map = new HashMap<String, Object>(m.size());
for (final Entry<?, ?> e : m.entrySet()) { for (final Entry<?, ?> e : m.entrySet()) {
if(e.getKey() == null) {
throw new NullPointerException("Null key.");
}
final Object value = e.getValue(); final Object value = e.getValue();
if (value != null) { if (value != null) {
this.map.put(String.valueOf(e.getKey()), wrap(value)); this.map.put(String.valueOf(e.getKey()), wrap(value));
@@ -298,13 +306,47 @@ public class JSONObject {
* prefix. If the second remaining character is not upper case, then the * prefix. If the second remaining character is not upper case, then the
* first character is converted to lower case. * first character is converted to lower case.
* <p> * <p>
* Methods that are <code>static</code>, return <code>void</code>,
* have parameters, or are "bridge" methods, are ignored.
* <p>
* For example, if an object has a method named <code>"getName"</code>, and * For example, if an object has a method named <code>"getName"</code>, and
* if the result of calling <code>object.getName()</code> is * if the result of calling <code>object.getName()</code> is
* <code>"Larry Fine"</code>, then the JSONObject will contain * <code>"Larry Fine"</code>, then the JSONObject will contain
* <code>"name": "Larry Fine"</code>. * <code>"name": "Larry Fine"</code>.
* <p> * <p>
* Methods that return <code>void</code> as well as <code>static</code> * The {@link JSONPropertyName} annotation can be used on a bean getter to
* methods are ignored. * override key name used in the JSONObject. For example, using the object
* above with the <code>getName</code> method, if we annotated it with:
* <pre>
* &#64;JSONPropertyName("FullName")
* public String getName() { return this.name; }
* </pre>
* The resulting JSON object would contain <code>"FullName": "Larry Fine"</code>
* <p>
* Similarly, the {@link JSONPropertyName} annotation can be used on non-
* <code>get</code> and <code>is</code> methods. We can also override key
* name used in the JSONObject as seen below even though the field would normally
* be ignored:
* <pre>
* &#64;JSONPropertyName("FullName")
* public String fullName() { return this.name; }
* </pre>
* The resulting JSON object would contain <code>"FullName": "Larry Fine"</code>
* <p>
* The {@link JSONPropertyIgnore} annotation can be used to force the bean property
* to not be serialized into JSON. If both {@link JSONPropertyIgnore} and
* {@link JSONPropertyName} are defined on the same method, a depth comparison is
* performed and the one closest to the concrete class being serialized is used.
* If both annotations are at the same level, then the {@link JSONPropertyIgnore}
* annotation takes precedent and the field is not serialized.
* For example, the following declaration would prevent the <code>getName</code>
* method from being serialized:
* <pre>
* &#64;JSONPropertyName("FullName")
* &#64;JSONPropertyIgnore
* public String getName() { return this.name; }
* </pre>
* <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
@@ -428,7 +470,9 @@ public class JSONObject {
* An object to be accumulated under the key. * An object to be accumulated under the key.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the value is an invalid number or if the key is null. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject accumulate(String key, Object value) throws JSONException { public JSONObject accumulate(String key, Object value) throws JSONException {
testValidity(value); testValidity(value);
@@ -457,8 +501,10 @@ public class JSONObject {
* An object to be accumulated under the key. * An object to be accumulated under the key.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the key is null or if the current value associated with * If the value is non-finite number or if the current value associated with
* the key is not a JSONArray. * the key is not a JSONArray.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject append(String key, Object value) throws JSONException { public JSONObject append(String key, Object value) throws JSONException {
testValidity(value); testValidity(value);
@@ -764,11 +810,10 @@ public class JSONObject {
* @return An array of field names, or null if there are no names. * @return An array of field names, or null if there are no names.
*/ */
public static String[] getNames(JSONObject jo) { public static String[] getNames(JSONObject jo) {
int length = jo.length(); if (jo.isEmpty()) {
if (length == 0) {
return null; return null;
} }
return jo.keySet().toArray(new String[length]); return jo.keySet().toArray(new String[jo.length()]);
} }
/** /**
@@ -856,13 +901,13 @@ public class JSONObject {
} }
/** /**
* Determine if the value associated with the key is null or if there is no * Determine if the value associated with the key is <code>null</code> or if there is no
* value. * value.
* *
* @param key * @param key
* A key string. * A key string.
* @return true if there is no value associated with the key or if the value * @return true if there is no value associated with the key or if the value
* is the JSONObject.NULL object. * is the JSONObject.NULL object.
*/ */
public boolean isNull(String key) { public boolean isNull(String key) {
return JSONObject.NULL.equals(this.opt(key)); return JSONObject.NULL.equals(this.opt(key));
@@ -917,12 +962,21 @@ public class JSONObject {
return this.map.size(); return this.map.size();
} }
/**
* Check if JSONObject is empty.
*
* @return true if JSONObject is empty, otherwise false.
*/
public boolean isEmpty() {
return map.isEmpty();
}
/** /**
* Produce a JSONArray containing the names of the elements of this * Produce a JSONArray containing the names of the elements of this
* JSONObject. * JSONObject.
* *
* @return A JSONArray containing the key strings, or null if the JSONObject * @return A JSONArray containing the key strings, or null if the JSONObject
* is empty. * is empty.
*/ */
public JSONArray names() { public JSONArray names() {
if(this.map.isEmpty()) { if(this.map.isEmpty()) {
@@ -1409,8 +1463,8 @@ public class JSONObject {
} }
/** /**
* Populates the internal map of the JSONObject with the bean properties. * Populates the internal map of the JSONObject with the bean properties. The
* The bean can not be recursive. * bean can not be recursive.
* *
* @see JSONObject#JSONObject(Object) * @see JSONObject#JSONObject(Object)
* *
@@ -1420,49 +1474,31 @@ public class JSONObject {
private void populateMap(Object bean) { private void populateMap(Object bean) {
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.
boolean includeSuperClass = klass.getClassLoader() != null; boolean includeSuperClass = klass.getClassLoader() != null;
Method[] methods = includeSuperClass ? klass.getMethods() : klass Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods();
.getDeclaredMethods();
for (final Method method : methods) { for (final Method method : methods) {
final int modifiers = method.getModifiers(); final int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) if (Modifier.isPublic(modifiers)
&& !Modifier.isStatic(modifiers) && !Modifier.isStatic(modifiers)
&& method.getParameterTypes().length == 0 && method.getParameterTypes().length == 0
&& !method.isBridge() && !method.isBridge()
&& method.getReturnType() != Void.TYPE ) { && method.getReturnType() != Void.TYPE
final String name = method.getName(); && isValidMethodName(method.getName())) {
String key; final String key = getKeyNameFromMethod(method);
if (name.startsWith("get")) { if (key != null && !key.isEmpty()) {
if ("getClass".equals(name) || "getDeclaringClass".equals(name)) {
continue;
}
key = name.substring(3);
} else if (name.startsWith("is")) {
key = name.substring(2);
} else {
continue;
}
if (key.length() > 0
&& Character.isUpperCase(key.charAt(0))) {
if (key.length() == 1) {
key = key.toLowerCase(Locale.ROOT);
} else if (!Character.isUpperCase(key.charAt(1))) {
key = key.substring(0, 1).toLowerCase(Locale.ROOT)
+ key.substring(1);
}
try { try {
final Object result = method.invoke(bean); final Object result = method.invoke(bean);
if (result != null) { if (result != null) {
this.map.put(key, wrap(result)); this.map.put(key, wrap(result));
// we don't use the result anywhere outside of wrap // 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 it's a resource we should be sure to close it
if(result instanceof Closeable) { // after calling toString
if (result instanceof Closeable) {
try { try {
((Closeable)result).close(); ((Closeable) result).close();
} catch (IOException ignore) { } catch (IOException ignore) {
} }
} }
@@ -1476,6 +1512,162 @@ public class JSONObject {
} }
} }
private boolean isValidMethodName(String name) {
return !"getClass".equals(name) && !"getDeclaringClass".equals(name);
}
private String getKeyNameFromMethod(Method method) {
final int ignoreDepth = getAnnotationDepth(method, JSONPropertyIgnore.class);
if (ignoreDepth > 0) {
final int forcedNameDepth = getAnnotationDepth(method, JSONPropertyName.class);
if (forcedNameDepth < 0 || ignoreDepth <= forcedNameDepth) {
// the hierarchy asked to ignore, and the nearest name override
// was higher or non-existent
return null;
}
}
JSONPropertyName annotation = getAnnotation(method, JSONPropertyName.class);
if (annotation != null && annotation.value() != null && !annotation.value().isEmpty()) {
return annotation.value();
}
String key;
final String name = method.getName();
if (name.startsWith("get") && name.length() > 3) {
key = name.substring(3);
} else if (name.startsWith("is") && name.length() > 2) {
key = name.substring(2);
} else {
return null;
}
// if the first letter in the key is not uppercase, then skip.
// This is to maintain backwards compatibility before PR406
// (https://github.com/stleary/JSON-java/pull/406/)
if (Character.isLowerCase(key.charAt(0))) {
return null;
}
if (key.length() == 1) {
key = key.toLowerCase(Locale.ROOT);
} else if (!Character.isUpperCase(key.charAt(1))) {
key = key.substring(0, 1).toLowerCase(Locale.ROOT) + key.substring(1);
}
return key;
}
/**
* Searches the class hierarchy to see if the method or it's super
* implementations and interfaces has the annotation.
*
* @param <A>
* type of the annotation
*
* @param m
* method to check
* @param annotationClass
* annotation to look for
* @return the {@link Annotation} if the annotation exists on the current method
* or one of it's super class definitions
*/
private static <A extends Annotation> A getAnnotation(final Method m, final Class<A> annotationClass) {
// if we have invalid data the result is null
if (m == null || annotationClass == null) {
return null;
}
if (m.isAnnotationPresent(annotationClass)) {
return m.getAnnotation(annotationClass);
}
// if we've already reached the Object class, return null;
Class<?> c = m.getDeclaringClass();
if (c.getSuperclass() == null) {
return null;
}
// check directly implemented interfaces for the method being checked
for (Class<?> i : c.getInterfaces()) {
try {
Method im = i.getMethod(m.getName(), m.getParameterTypes());
return getAnnotation(im, annotationClass);
} catch (final SecurityException ex) {
continue;
} catch (final NoSuchMethodException ex) {
continue;
}
}
try {
return getAnnotation(
c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()),
annotationClass);
} catch (final SecurityException ex) {
return null;
} catch (final NoSuchMethodException ex) {
return null;
}
}
/**
* Searches the class hierarchy to see if the method or it's super
* 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
* annotation to look for
* @return Depth of the annotation or -1 if the annotation is not on the method.
*/
private static int getAnnotationDepth(final Method m, final Class<? extends Annotation> annotationClass) {
// if we have invalid data the result is -1
if (m == null || annotationClass == null) {
return -1;
}
if (m.isAnnotationPresent(annotationClass)) {
return 1;
}
// if we've already reached the Object class, return -1;
Class<?> c = m.getDeclaringClass();
if (c.getSuperclass() == null) {
return -1;
}
// check directly implemented interfaces for the method being checked
for (Class<?> i : c.getInterfaces()) {
try {
Method im = i.getMethod(m.getName(), m.getParameterTypes());
int d = getAnnotationDepth(im, annotationClass);
if (d > 0) {
// since the annotation was on the interface, add 1
return d + 1;
}
} catch (final SecurityException ex) {
continue;
} catch (final NoSuchMethodException ex) {
continue;
}
}
try {
int d = getAnnotationDepth(
c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()),
annotationClass);
if (d > 0) {
// since the annotation was on the superclass, add 1
return d + 1;
}
return -1;
} catch (final SecurityException ex) {
return -1;
} catch (final NoSuchMethodException ex) {
return -1;
}
}
/** /**
* Put a key/boolean pair in the JSONObject. * Put a key/boolean pair in the JSONObject.
* *
@@ -1485,11 +1677,12 @@ public class JSONObject {
* A boolean which is the value. * A boolean which is the value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the key is null. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, boolean value) throws JSONException { public JSONObject put(String key, boolean value) throws JSONException {
this.put(key, value ? Boolean.TRUE : Boolean.FALSE); return this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
return this;
} }
/** /**
@@ -1502,10 +1695,12 @@ public class JSONObject {
* A Collection value. * A Collection value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, Collection<?> value) throws JSONException { public JSONObject put(String key, Collection<?> value) throws JSONException {
this.put(key, new JSONArray(value)); return this.put(key, new JSONArray(value));
return this;
} }
/** /**
@@ -1517,11 +1712,12 @@ public class JSONObject {
* A double which is the value. * A double which is the value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the key is null or if the number is invalid. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, double value) throws JSONException { public JSONObject put(String key, double value) throws JSONException {
this.put(key, Double.valueOf(value)); return this.put(key, Double.valueOf(value));
return this;
} }
/** /**
@@ -1533,11 +1729,12 @@ public class JSONObject {
* A float which is the value. * A float which is the value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the key is null or if the number is invalid. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, float value) throws JSONException { public JSONObject put(String key, float value) throws JSONException {
this.put(key, Float.valueOf(value)); return this.put(key, Float.valueOf(value));
return this;
} }
/** /**
@@ -1549,11 +1746,12 @@ public class JSONObject {
* An int which is the value. * An int which is the value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the key is null. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, int value) throws JSONException { public JSONObject put(String key, int value) throws JSONException {
this.put(key, Integer.valueOf(value)); return this.put(key, Integer.valueOf(value));
return this;
} }
/** /**
@@ -1565,11 +1763,12 @@ public class JSONObject {
* A long which is the value. * A long which is the value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the key is null. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, long value) throws JSONException { public JSONObject put(String key, long value) throws JSONException {
this.put(key, Long.valueOf(value)); return this.put(key, Long.valueOf(value));
return this;
} }
/** /**
@@ -1582,14 +1781,16 @@ public class JSONObject {
* A Map value. * A Map value.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, Map<?, ?> value) throws JSONException { public JSONObject put(String key, Map<?, ?> value) throws JSONException {
this.put(key, new JSONObject(value)); return this.put(key, new JSONObject(value));
return this;
} }
/** /**
* Put a key/value pair in the JSONObject. If the value is null, then the * Put a key/value pair in the JSONObject. If the value is <code>null</code>, then the
* key will be removed from the JSONObject if it is present. * key will be removed from the JSONObject if it is present.
* *
* @param key * @param key
@@ -1600,7 +1801,9 @@ public class JSONObject {
* String, or the JSONObject.NULL object. * String, or the JSONObject.NULL object.
* @return this. * @return this.
* @throws JSONException * @throws JSONException
* If the value is non-finite number or if the key is null. * If the value is non-finite number.
* @throws NullPointerException
* If the key is <code>null</code>.
*/ */
public JSONObject put(String key, Object value) throws JSONException { public JSONObject put(String key, Object value) throws JSONException {
if (key == null) { if (key == null) {
@@ -1631,7 +1834,7 @@ public class JSONObject {
if (this.opt(key) != null) { if (this.opt(key) != null) {
throw new JSONException("Duplicate key \"" + key + "\""); throw new JSONException("Duplicate key \"" + key + "\"");
} }
this.put(key, value); return this.put(key, value);
} }
return this; return this;
} }
@@ -1652,7 +1855,7 @@ public class JSONObject {
*/ */
public JSONObject putOpt(String key, Object value) throws JSONException { public JSONObject putOpt(String key, Object value) throws JSONException {
if (key != null && value != null) { if (key != null && value != null) {
this.put(key, value); return this.put(key, value);
} }
return this; return this;
} }
@@ -1753,7 +1956,7 @@ public class JSONObject {
} }
public static Writer quote(String string, Writer w) throws IOException { public static Writer quote(String string, Writer w) throws IOException {
if (string == null || string.length() == 0) { if (string == null || string.isEmpty()) {
w.write("\"\""); w.write("\"\"");
return w; return w;
} }
@@ -1844,7 +2047,7 @@ public class JSONObject {
Object valueThis = entry.getValue(); Object valueThis = entry.getValue();
Object valueOther = ((JSONObject)other).get(name); Object valueOther = ((JSONObject)other).get(name);
if(valueThis == valueOther) { if(valueThis == valueOther) {
return true; continue;
} }
if(valueThis == null) { if(valueThis == null) {
return false; return false;
@@ -1950,6 +2153,8 @@ public class JSONObject {
* A String. * A String.
* @return A simple JSON value. * @return A simple JSON value.
*/ */
// 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) { public static Object stringToValue(String string) {
if (string.equals("")) { if (string.equals("")) {
return string; return string;
@@ -2030,7 +2235,7 @@ public class JSONObject {
* If any of the values are non-finite numbers. * If any of the values are non-finite numbers.
*/ */
public JSONArray toJSONArray(JSONArray names) throws JSONException { public JSONArray toJSONArray(JSONArray names) throws JSONException {
if (names == null || names.length() == 0) { if (names == null || names.isEmpty()) {
return null; return null;
} }
JSONArray ja = new JSONArray(); JSONArray ja = new JSONArray();
@@ -2120,59 +2325,15 @@ public class JSONObject {
* If the value is or contains an invalid number. * If the value is or contains an invalid number.
*/ */
public static String valueToString(Object value) throws JSONException { public static String valueToString(Object value) throws JSONException {
if (value == null || value.equals(null)) { // moves the implementation to JSONWriter as:
return "null"; // 1. It makes more sense to be part of the writer class
} // 2. For Android support this method is not available. By implementing it in the Writer
if (value instanceof JSONString) { // Android users can use the writer with the built in Android JSONObject implementation.
Object object; return JSONWriter.valueToString(value);
try {
object = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
if (object instanceof String) {
return (String) object;
}
throw new JSONException("Bad value from toJSONString: " + object);
}
if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
final String numberAsString = numberToString((Number) value);
try {
// Use the BigDecimal constructor for it's parser to validate the format.
@SuppressWarnings("unused")
BigDecimal unused = new BigDecimal(numberAsString);
// 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 quote(numberAsString);
}
}
if (value instanceof Boolean || value instanceof JSONObject
|| value instanceof JSONArray) {
return value.toString();
}
if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
return new JSONObject(map).toString();
}
if (value instanceof Collection) {
Collection<?> coll = (Collection<?>) value;
return new JSONArray(coll).toString();
}
if (value.getClass().isArray()) {
return new JSONArray(value).toString();
}
if(value instanceof Enum<?>){
return quote(((Enum<?>)value).name());
}
return quote(value.toString());
} }
/** /**
* Wrap an object, if necessary. If the object is null, return the NULL * Wrap an object, if necessary. If the object is <code>null</code>, return the NULL
* object. If it is an array or collection, wrap it in a JSONArray. If it is * object. If it is an array or collection, wrap it in a JSONArray. If it is
* a map, wrap it in a JSONObject. If it is a standard property (Double, * a map, wrap it in a JSONObject. If it is a standard property (Double,
* String, et al) then it is already wrapped. Otherwise, if it comes from * String, et al) then it is already wrapped. Otherwise, if it comes from
@@ -2254,7 +2415,7 @@ public class JSONObject {
// not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary
final String numberAsString = numberToString((Number) value); final String numberAsString = numberToString((Number) value);
try { try {
// Use the BigDecimal constructor for it's parser to validate the format. // Use the BigDecimal constructor for its parser to validate the format.
@SuppressWarnings("unused") @SuppressWarnings("unused")
BigDecimal testNum = new BigDecimal(numberAsString); BigDecimal testNum = new BigDecimal(numberAsString);
// Close enough to a JSON number that we will use it unquoted // Close enough to a JSON number that we will use it unquoted

View File

@@ -5,7 +5,9 @@ import static java.lang.String.format;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/* /*
Copyright (c) 2002 JSON.org Copyright (c) 2002 JSON.org
@@ -156,9 +158,28 @@ public class JSONPointer {
throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'");
} }
this.refTokens = new ArrayList<String>(); this.refTokens = new ArrayList<String>();
for (String token : refs.split("/")) { int slashIdx = -1;
this.refTokens.add(unescape(token)); int prevSlashIdx = 0;
} do {
prevSlashIdx = slashIdx + 1;
slashIdx = refs.indexOf('/', prevSlashIdx);
if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) {
// found 2 slashes in a row ( obj//next )
// or single slash at the end of a string ( obj/test/ )
this.refTokens.add("");
} else if (slashIdx >= 0) {
final String token = refs.substring(prevSlashIdx, slashIdx);
this.refTokens.add(unescape(token));
} else {
// last item after separator, or no separator at all.
final String token = refs.substring(prevSlashIdx);
this.refTokens.add(unescape(token));
}
} while (slashIdx >= 0);
// using split does not take into account consecutive separators or "ending nulls"
//for (String token : refs.split("/")) {
// this.refTokens.add(unescape(token));
//}
} }
public JSONPointer(List<String> refTokens) { public JSONPointer(List<String> refTokens) {
@@ -181,7 +202,7 @@ public class JSONPointer {
* @return the result of the evaluation * @return the result of the evaluation
* @throws JSONPointerException if an error occurs during evaluation * @throws JSONPointerException if an error occurs during evaluation
*/ */
public Object queryFrom(Object document) { public Object queryFrom(Object document) throws JSONPointerException {
if (this.refTokens.isEmpty()) { if (this.refTokens.isEmpty()) {
return document; return document;
} }
@@ -205,10 +226,9 @@ public class JSONPointer {
* @param current the JSONArray to be evaluated * @param current the JSONArray to be evaluated
* @param indexToken the array index in string form * @param indexToken the array index in string form
* @return the matched object. If no matching item is found a * @return the matched object. If no matching item is found a
* JSONPointerException is thrown * @throws JSONPointerException is thrown if the index is out of bounds
*/ */
@SuppressWarnings("boxing") private Object readByIndexToken(Object current, String indexToken) throws JSONPointerException {
private Object readByIndexToken(Object current, String indexToken) {
try { try {
int index = Integer.parseInt(indexToken); int index = Integer.parseInt(indexToken);
JSONArray currentArr = (JSONArray) current; JSONArray currentArr = (JSONArray) current;
@@ -216,7 +236,11 @@ public class JSONPointer {
throw new JSONPointerException(format("index %d is out of bounds - the array has %d elements", index, throw new JSONPointerException(format("index %d is out of bounds - the array has %d elements", index,
currentArr.length())); currentArr.length()));
} }
return currentArr.get(index); try {
return currentArr.get(index);
} catch (JSONException e) {
throw new JSONPointerException("Error reading value at index position " + index, e);
}
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new JSONPointerException(format("%s is not an array index", indexToken), e); throw new JSONPointerException(format("%s is not an array index", indexToken), e);
} }

43
JSONPropertyIgnore.java Normal file
View File

@@ -0,0 +1,43 @@
package org.json;
/*
Copyright (c) 2018 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/**
* Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. If this annotation is
* present at any level in the class hierarchy, then the method will
* not be serialized from the bean into the JSONObject.
*/
public @interface JSONPropertyIgnore { }

47
JSONPropertyName.java Normal file
View File

@@ -0,0 +1,47 @@
package org.json;
/*
Copyright (c) 2018 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/**
* Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. A value set to empty string <code>""</code>
* will have the Bean parser fall back to the default field name processing.
*/
public @interface JSONPropertyName {
/**
* @return The name of the property as to be used in the JSON Object.
*/
String value();
}

View File

@@ -1,6 +1,9 @@
package org.json; package org.json;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
/* /*
Copyright (c) 2006 JSON.org Copyright (c) 2006 JSON.org
@@ -117,6 +120,9 @@ public class JSONWriter {
} }
this.writer.append(string); this.writer.append(string);
} catch (IOException e) { } catch (IOException e) {
// Android as of API 25 does not support this exception constructor
// however we won't worry about it. If an exception is happening here
// it will just throw a "Method not found" exception instead.
throw new JSONException(e); throw new JSONException(e);
} }
if (this.mode == 'o') { if (this.mode == 'o') {
@@ -164,6 +170,9 @@ public class JSONWriter {
try { try {
this.writer.append(c); this.writer.append(c);
} catch (IOException e) { } catch (IOException e) {
// Android as of API 25 does not support this exception constructor
// however we won't worry about it. If an exception is happening here
// it will just throw a "Method not found" exception instead.
throw new JSONException(e); throw new JSONException(e);
} }
this.comma = true; this.comma = true;
@@ -204,7 +213,12 @@ public class JSONWriter {
} }
if (this.mode == 'k') { if (this.mode == 'k') {
try { try {
this.stack[this.top - 1].putOnce(string, Boolean.TRUE); JSONObject topObject = this.stack[this.top - 1];
// don't use the built in putOnce method to maintain Android support
if(topObject.has(string)) {
throw new JSONException("Duplicate key \"" + string + "\"");
}
topObject.put(string, true);
if (this.comma) { if (this.comma) {
this.writer.append(','); this.writer.append(',');
} }
@@ -214,6 +228,9 @@ public class JSONWriter {
this.mode = 'o'; this.mode = 'o';
return this; return this;
} catch (IOException e) { } catch (IOException e) {
// Android as of API 25 does not support this exception constructor
// however we won't worry about it. If an exception is happening here
// it will just throw a "Method not found" exception instead.
throw new JSONException(e); throw new JSONException(e);
} }
} }
@@ -280,6 +297,81 @@ public class JSONWriter {
this.top += 1; this.top += 1;
} }
/**
* Make a JSON text of an Object value. If the object has an
* value.toJSONString() method, then that method will be used to produce the
* JSON text. The method is required to produce a strictly conforming text.
* If the object does not contain a toJSONString method (which is the most
* common case), then a text will be produced by other means. If the value
* is an array or Collection, then a JSONArray will be made from it and its
* toJSONString method will be called. If the value is a MAP, then a
* JSONObject will be made from it and its toJSONString method will be
* called. Otherwise, the value's toString method will be called, and the
* result will be quoted.
*
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @param value
* The value to be serialized.
* @return a printable, displayable, transmittable representation of the
* object, beginning with <code>{</code>&nbsp;<small>(left
* brace)</small> and ending with <code>}</code>&nbsp;<small>(right
* brace)</small>.
* @throws JSONException
* If the value is or contains an invalid number.
*/
public static String valueToString(Object value) throws JSONException {
if (value == null || value.equals(null)) {
return "null";
}
if (value instanceof JSONString) {
Object object;
try {
object = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
if (object instanceof String) {
return (String) object;
}
throw new JSONException("Bad value from toJSONString: " + object);
}
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);
// 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);
}
}
if (value instanceof Boolean || value instanceof JSONObject
|| value instanceof JSONArray) {
return value.toString();
}
if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
return new JSONObject(map).toString();
}
if (value instanceof Collection) {
Collection<?> coll = (Collection<?>) value;
return new JSONArray(coll).toString();
}
if (value.getClass().isArray()) {
return new JSONArray(value).toString();
}
if(value instanceof Enum<?>){
return JSONObject.quote(((Enum<?>)value).name());
}
return JSONObject.quote(value.toString());
}
/** /**
* Append either the value <code>true</code> or the value * Append either the value <code>true</code> or the value
@@ -299,7 +391,7 @@ public class JSONWriter {
* @throws JSONException If the number is not finite. * @throws JSONException If the number is not finite.
*/ */
public JSONWriter value(double d) throws JSONException { public JSONWriter value(double d) throws JSONException {
return this.value(new Double(d)); return this.value(Double.valueOf(d));
} }
/** /**
@@ -321,6 +413,6 @@ public class JSONWriter {
* @throws JSONException If the value is out of sequence. * @throws JSONException If the value is out of sequence.
*/ */
public JSONWriter value(Object object) throws JSONException { public JSONWriter value(Object object) throws JSONException {
return this.append(JSONObject.valueToString(object)); return this.append(valueToString(object));
} }
} }

View File

@@ -25,7 +25,6 @@ SOFTWARE.
*/ */
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
/** /**
@@ -41,7 +40,9 @@ public class Property {
* @throws JSONException * @throws JSONException
*/ */
public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException { public static JSONObject toJSONObject(java.util.Properties properties) throws JSONException {
JSONObject jo = new JSONObject(properties == null ? 0 : properties.size()); // can't use the new constructor for Android support
// JSONObject jo = new JSONObject(properties == null ? 0 : properties.size());
JSONObject jo = new JSONObject();
if (properties != null && !properties.isEmpty()) { if (properties != null && !properties.isEmpty()) {
Enumeration<?> enumProperties = properties.propertyNames(); Enumeration<?> enumProperties = properties.propertyNames();
while(enumProperties.hasMoreElements()) { while(enumProperties.hasMoreElements()) {
@@ -61,10 +62,11 @@ public class Property {
public static Properties toProperties(JSONObject jo) throws JSONException { public static Properties toProperties(JSONObject jo) throws JSONException {
Properties properties = new Properties(); Properties properties = new Properties();
if (jo != null) { if (jo != null) {
for (final Entry<String, ?> entry : jo.entrySet()) { // Don't use the new entrySet API to maintain Android support
Object value = entry.getValue(); for (final String key : jo.keySet()) {
Object value = jo.opt(key);
if (!JSONObject.NULL.equals(value)) { if (!JSONObject.NULL.equals(value)) {
properties.put(entry.getKey(), value.toString()); properties.put(key, value.toString());
} }
} }
} }

114
README
View File

@@ -1,114 +0,0 @@
JSON in Java [package org.json]
JSON is a light-weight, language independent, data interchange format.
See http://www.JSON.org/
The files in this package implement JSON encoders/decoders in Java.
It also includes the capability to convert between JSON and XML, HTTP
headers, Cookies, and CDL.
This is a reference implementation. There is a large number of JSON packages
in Java. Perhaps someday the Java community will standardize on one. Until
then, choose carefully.
The license includes this restriction: "The software shall be used for good,
not evil." If your conscience cannot live with that, then choose a different
package.
The package compiles on Java 1.6-1.8.
JSONObject.java: The JSONObject can parse text from a String or a JSONTokener
to produce a map-like object. The object provides methods for manipulating its
contents, and for producing a JSON compliant object serialization.
JSONArray.java: The JSONArray can parse text from a String or a JSONTokener
to produce a vector-like object. The object provides methods for manipulating
its contents, and for producing a JSON compliant array serialization.
JSONTokener.java: The JSONTokener breaks a text into a sequence of individual
tokens. It can be constructed from a String, Reader, or InputStream.
JSONException.java: The JSONException is the standard exception type thrown
by this package.
JSONPointer.java: Implementation of
[JSON Pointer (RFC 6901)](https://tools.ietf.org/html/rfc6901). Supports
JSON Pointers both in the form of string representation and URI fragment
representation.
JSONString.java: The JSONString interface requires a toJSONString method,
allowing an object to provide its own serialization.
JSONStringer.java: The JSONStringer provides a convenient facility for
building JSON strings.
JSONWriter.java: The JSONWriter provides a convenient facility for building
JSON text through a writer.
CDL.java: CDL provides support for converting between JSON and comma
delimited lists.
Cookie.java: Cookie provides support for converting between JSON and cookies.
CookieList.java: CookieList provides support for converting between JSON and
cookie lists.
HTTP.java: HTTP provides support for converting between JSON and HTTP headers.
HTTPTokener.java: HTTPTokener extends JSONTokener for parsing HTTP headers.
XML.java: XML provides support for converting between JSON and XML.
JSONML.java: JSONML provides support for converting between JSONML and XML.
XMLTokener.java: XMLTokener extends JSONTokener for parsing XML text.
Unit tests are maintained in a separate project. Contributing developers can test
JSON-java pull requests with the code in this project:
https://github.com/stleary/JSON-Java-unit-test
Numeric types in this package comply with ECMA-404: The JSON Data Interchange Format
(http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) and
RFC 7159: The JavaScript Object Notation (JSON) Data Interchange Format
(https://tools.ietf.org/html/rfc7159#section-6).
This package fully supports Integer, Long, and Double Java types. Partial support
for BigInteger and BigDecimal values in JSONObject and JSONArray objects is provided
in the form of get(), opt(), and put() API methods.
Although 1.6 compatibility is currently supported, it is not a project goal and may be
removed in some future release.
In compliance with RFC7159 page 10 section 9, the parser is more lax with what is valid
JSON than the Generator. For Example, the tab character (U+0009) is allowed when reading
JSON Text strings, but when output by the Generator, tab is properly converted to \t in
the string. Other instances may occur where reading invalid JSON text does not cause an
error to be generated. Malformed JSON Texts such as missing end " (quote) on strings or
invalid number formats (1.2e6.3) will cause errors as such documents can not be read
reliably.
Release history:
20171018 Checkpoint for recent commits.
20170516 Roll up recent commits.
20160810 Revert code that was breaking opt*() methods.
20160807 This release contains a bug in the JSONObject.opt*() and JSONArray.opt*() methods,
it is not recommended for use.
Java 1.6 compatability fixed, JSONArray.toList() and JSONObject.toMap(),
RFC4180 compatibility, JSONPointer, some exception fixes, optional XML type conversion.
Contains the latest code as of 7 Aug, 2016
20160212 Java 1.6 compatibility, OSGi bundle. Contains the latest code as of 12 Feb, 2016.
20151123 JSONObject and JSONArray initialization with generics. Contains the
latest code as of 23 Nov, 2015.
20150729 Checkpoint for Maven central repository release. Contains the latest code
as of 29 July, 2015.
JSON-java releases can be found by searching the Maven repository for groupId "org.json"
and artifactId "json". For example:
https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.json%22%20AND%20a%3A%22json%22

136
README.md Normal file
View File

@@ -0,0 +1,136 @@
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)
JSON is a light-weight, language independent, data interchange format.
See http://www.JSON.org/
The files in this package implement JSON encoders/decoders in Java.
It also includes the capability to convert between JSON and XML, HTTP
headers, Cookies, and CDL.
This is a reference implementation. There is a large number of JSON packages
in Java. Perhaps someday the Java community will standardize on one. Until
then, choose carefully.
The license includes this restriction: "The software shall be used for good,
not evil." If your conscience cannot live with that, then choose a different
package.
The package compiles on Java 1.6-1.8.
**JSONObject.java**: The `JSONObject` can parse text from a `String` or a `JSONTokener`
to produce a map-like object. The object provides methods for manipulating its
contents, and for producing a JSON compliant object serialization.
**JSONArray.java**: The `JSONArray` can parse text from a String or a `JSONTokener`
to produce a vector-like object. The object provides methods for manipulating
its contents, and for producing a JSON compliant array serialization.
**JSONTokener.java**: The `JSONTokener` breaks a text into a sequence of individual
tokens. It can be constructed from a `String`, `Reader`, or `InputStream`.
**JSONException.java**: The `JSONException` is the standard exception type thrown
by this package.
**JSONPointer.java**: Implementation of
[JSON Pointer (RFC 6901)](https://tools.ietf.org/html/rfc6901). Supports
JSON Pointers both in the form of string representation and URI fragment
representation.
**JSONPropertyIgnore.java**: Annotation class that can be used on Java Bean getter methods.
When used on a bean method that would normally be serialized into a `JSONObject`, it
overrides the getter-to-key-name logic and forces the property to be excluded from the
resulting `JSONObject`.
**JSONPropertyName.java**: Annotation class that can be used on Java Bean getter methods.
When used on a bean method that would normally be serialized into a `JSONObject`, it
overrides the getter-to-key-name logic and uses the value of the annotation. The Bean
processor will look through the class hierarchy. This means you can use the annotation on
a base class or interface and the value of the annotation will be used even if the getter
is overridden in a child class.
**JSONString.java**: The `JSONString` interface requires a `toJSONString` method,
allowing an object to provide its own serialization.
**JSONStringer.java**: The `JSONStringer` provides a convenient facility for
building JSON strings.
**JSONWriter.java**: The `JSONWriter` provides a convenient facility for building
JSON text through a writer.
**CDL.java**: `CDL` provides support for converting between JSON and comma
delimited lists.
**Cookie.java**: `Cookie` provides support for converting between JSON and cookies.
**CookieList.java**: `CookieList` provides support for converting between JSON and
cookie lists.
**HTTP.java**: `HTTP` provides support for converting between JSON and HTTP headers.
**HTTPTokener.java**: `HTTPTokener` extends `JSONTokener` for parsing HTTP headers.
**XML.java**: `XML` provides support for converting between JSON and XML.
**JSONML.java**: `JSONML` provides support for converting between JSONML and XML.
**XMLTokener.java**: `XMLTokener` extends `JSONTokener` for parsing XML text.
Unit tests are maintained in a separate project. Contributing developers can test
JSON-java pull requests with the code in this project:
https://github.com/stleary/JSON-Java-unit-test
Numeric types in this package comply with
[ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) and
[RFC 7159: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc7159#section-6).
This package fully supports `Integer`, `Long`, and `Double` Java types. Partial support
for `BigInteger` and `BigDecimal` values in `JSONObject` and `JSONArray` objects is provided
in the form of `get()`, `opt()`, and `put()` API methods.
Although 1.6 compatibility is currently supported, it is not a project goal and may be
removed in some future release.
In compliance with RFC7159 page 10 section 9, the parser is more lax with what is valid
JSON than the Generator. For Example, the tab character (U+0009) is allowed when reading
JSON Text strings, but when output by the Generator, tab is properly converted to \t in
the string. Other instances may occur where reading invalid JSON text does not cause an
error to be generated. Malformed JSON Texts such as missing end " (quote) on strings or
invalid number formats (1.2e6.3) will cause errors as such documents can not be read
reliably.
Release history:
~~~
20180813 POM change to include Automatic-Module-Name (#431)
20180130 Recent commits
20171018 Checkpoint for recent commits.
20170516 Roll up recent commits.
20160810 Revert code that was breaking opt*() methods.
20160807 This release contains a bug in the JSONObject.opt*() and JSONArray.opt*() methods,
it is not recommended for use.
Java 1.6 compatability fixed, JSONArray.toList() and JSONObject.toMap(),
RFC4180 compatibility, JSONPointer, some exception fixes, optional XML type conversion.
Contains the latest code as of 7 Aug, 2016
20160212 Java 1.6 compatibility, OSGi bundle. Contains the latest code as of 12 Feb, 2016.
20151123 JSONObject and JSONArray initialization with generics. Contains the
latest code as of 23 Nov, 2015.
20150729 Checkpoint for Maven central repository release. Contains the latest code
as of 29 July, 2015.
~~~
JSON-java releases can be found by searching the Maven repository for groupId "org.json"
and artifactId "json". For example:
https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.json%22%20AND%20a%3A%22json%22

132
XML.java
View File

@@ -24,8 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
import java.io.Reader;
import java.io.StringReader;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map.Entry;
/** /**
* This provides static methods to convert an XML text into a JSONObject, and to * This provides static methods to convert an XML text into a JSONObject, and to
@@ -399,14 +400,56 @@ public class XML {
} }
/** /**
* This method is the same as {@link JSONObject#stringToValue(String)} * This method is the same as {@link JSONObject#stringToValue(String)}.
* except that this also tries to unescape String values.
* *
* @param string String to convert * @param string String to convert
* @return JSON value of this string or the string * @return JSON value of this string or the string
*/ */
// To maintain compatibility with the Android API, this method is a direct copy of
// the one in JSONObject. Changes made here should be reflected there.
public static Object stringToValue(String string) { public static Object stringToValue(String string) {
return JSONObject.stringToValue(string); if (string.equals("")) {
return string;
}
if (string.equalsIgnoreCase("true")) {
return Boolean.TRUE;
}
if (string.equalsIgnoreCase("false")) {
return Boolean.FALSE;
}
if (string.equalsIgnoreCase("null")) {
return JSONObject.NULL;
}
/*
* If it might be a number, try converting it. If a number cannot be
* produced, then the value will just be a 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:
// return stringToNumber(string);
if (string.indexOf('.') > -1 || string.indexOf('e') > -1
|| string.indexOf('E') > -1 || "-0".equals(string)) {
Double d = Double.valueOf(string);
if (!d.isInfinite() && !d.isNaN()) {
return d;
}
} else {
Long myLong = Long.valueOf(string);
if (string.equals(myLong.toString())) {
if (myLong.longValue() == myLong.intValue()) {
return Integer.valueOf(myLong.intValue());
}
return myLong;
}
}
} catch (Exception ignore) {
}
}
return string;
} }
/** /**
@@ -429,6 +472,56 @@ public class XML {
return toJSONObject(string, false); return toJSONObject(string, false);
} }
/**
* Convert a well-formed (but not necessarily valid) XML into a
* JSONObject. Some information may be lost in this transformation because
* JSON is a data format and XML is a document format. XML uses elements,
* attributes, and content text, while JSON uses unordered collections of
* name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code>
* are ignored.
*
* @param reader The XML source reader.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(Reader reader) throws JSONException {
return toJSONObject(reader, false);
}
/**
* Convert a well-formed (but not necessarily valid) XML into a
* JSONObject. Some information may be lost in this transformation because
* JSON is a data format and XML is a document format. XML uses elements,
* attributes, and content text, while JSON uses unordered collections of
* name/value pairs and arrays of values. JSON does not does not like to
* distinguish between elements and attributes. Sequences of similar
* elements are represented as JSONArrays. Content text may be placed in a
* "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code>
* are ignored.
*
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to
* numbers but will instead be the exact value as seen in the XML document.
*
* @param reader The XML source reader.
* @param keepStrings If true, then values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws JSONException {
JSONObject jo = new JSONObject();
XMLTokener x = new XMLTokener(reader);
while (x.more()) {
x.skipPast("<");
if(x.more()) {
parse(x, jo, null, keepStrings);
}
}
return jo;
}
/** /**
* Convert a well-formed (but not necessarily valid) XML string into a * Convert a well-formed (but not necessarily valid) XML string into a
@@ -452,13 +545,9 @@ public class XML {
* @throws JSONException Thrown if there is an errors while parsing the string * @throws JSONException Thrown if there is an errors while parsing the string
*/ */
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException { public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
JSONObject jo = new JSONObject(); return toJSONObject(new StringReader(string), keepStrings);
XMLTokener x = new XMLTokener(string);
while (x.more() && x.skipPast("<")) {
parse(x, jo, null, keepStrings);
}
return jo;
} }
/** /**
* Convert a JSONObject into a well-formed, element-normal XML string. * Convert a JSONObject into a well-formed, element-normal XML string.
* *
@@ -498,10 +587,10 @@ public class XML {
} }
// Loop thru the keys. // Loop thru the keys.
// don't use the new entrySet accessor to maintain Android Support
jo = (JSONObject) object; jo = (JSONObject) object;
for (final Entry<String, ?> entry : jo.entrySet()) { for (final String key : jo.keySet()) {
final String key = entry.getKey(); Object value = jo.opt(key);
Object value = entry.getValue();
if (value == null) { if (value == null) {
value = ""; value = "";
} else if (value.getClass().isArray()) { } else if (value.getClass().isArray()) {
@@ -512,13 +601,14 @@ public class XML {
if ("content".equals(key)) { if ("content".equals(key)) {
if (value instanceof JSONArray) { if (value instanceof JSONArray) {
ja = (JSONArray) value; ja = (JSONArray) value;
int i = 0; int jaLength = ja.length();
for (Object val : ja) { // don't use the new iterator API to maintain support for Android
for (int i = 0; i < jaLength; i++) {
if (i > 0) { if (i > 0) {
sb.append('\n'); sb.append('\n');
} }
Object val = ja.opt(i);
sb.append(escape(val.toString())); sb.append(escape(val.toString()));
i++;
} }
} else { } else {
sb.append(escape(value.toString())); sb.append(escape(value.toString()));
@@ -528,7 +618,10 @@ public class XML {
} else if (value instanceof JSONArray) { } else if (value instanceof JSONArray) {
ja = (JSONArray) value; ja = (JSONArray) value;
for (Object val : ja) { int jaLength = ja.length();
// don't use the new iterator API to maintain support for Android
for (int i = 0; i < jaLength; i++) {
Object val = ja.opt(i);
if (val instanceof JSONArray) { if (val instanceof JSONArray) {
sb.append('<'); sb.append('<');
sb.append(key); sb.append(key);
@@ -569,7 +662,10 @@ public class XML {
} else { } else {
ja = (JSONArray) object; ja = (JSONArray) object;
} }
for (Object val : ja) { int jaLength = ja.length();
// don't use the new iterator API to maintain support for Android
for (int i = 0; i < jaLength; i++) {
Object val = ja.opt(i);
// XML does not have good support for arrays. If an array // XML does not have good support for arrays. If an array
// appears in a place where XML is lacking, synthesize an // appears in a place where XML is lacking, synthesize an
// <array> element. // <array> element.

View File

@@ -24,6 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
import java.io.Reader;
/** /**
* The XMLTokener extends the JSONTokener to provide additional methods * The XMLTokener extends the JSONTokener to provide additional methods
* for the parsing of XML texts. * for the parsing of XML texts.
@@ -47,6 +49,14 @@ public class XMLTokener extends JSONTokener {
entity.put("quot", XML.QUOT); entity.put("quot", XML.QUOT);
} }
/**
* Construct an XMLTokener from a Reader.
* @param r A source reader.
*/
public XMLTokener(Reader r) {
super(r);
}
/** /**
* Construct an XMLTokener from a string. * Construct an XMLTokener from a string.
* @param s A source string. * @param s A source string.
@@ -326,9 +336,11 @@ public class XMLTokener extends JSONTokener {
* Skip characters until past the requested string. * Skip characters until past the requested string.
* If it is not found, we are left at the end of the source with a result of false. * If it is not found, we are left at the end of the source with a result of false.
* @param to A string to skip past. * @param to A string to skip past.
* @throws JSONException
*/ */
public boolean skipPast(String to) throws JSONException { // The Android implementation of JSONTokener has a public method of public void skipPast(String to)
// even though ours does not have that method, to have API compatibility, our method in the subclass
// should match.
public void skipPast(String to) {
boolean b; boolean b;
char c; char c;
int i; int i;
@@ -345,7 +357,7 @@ public class XMLTokener extends JSONTokener {
for (i = 0; i < length; i += 1) { for (i = 0; i < length; i += 1) {
c = next(); c = next();
if (c == 0) { if (c == 0) {
return false; return;
} }
circle[i] = c; circle[i] = c;
} }
@@ -372,14 +384,14 @@ public class XMLTokener extends JSONTokener {
/* If we exit the loop with b intact, then victory is ours. */ /* If we exit the loop with b intact, then victory is ours. */
if (b) { if (b) {
return true; return;
} }
/* Get the next character. If there isn't one, then defeat is ours. */ /* Get the next character. If there isn't one, then defeat is ours. */
c = next(); c = next();
if (c == 0) { if (c == 0) {
return false; return;
} }
/* /*
* Shove the character in the circle buffer and advance the * Shove the character in the circle buffer and advance the