1
0
Fork 0
mirror of https://github.com/ethauvin/JSON-java.git synced 2025-06-17 07:50:52 -07:00

Merge remote-tracking branch 'origin/master' into AndroidSupport

This commit is contained in:
John J. Aylward 2017-10-27 13:28:20 -04:00
commit 057e0c75ca
6 changed files with 187 additions and 82 deletions

View file

@ -1230,7 +1230,7 @@ public class JSONArray implements Iterable<Object> {
* Queries and returns a value from this object using {@code jsonPointer}, or * Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key. * returns null if the query fails due to a missing key.
* *
* @param The JSON pointer * @param jsonPointer The JSON pointer
* @return the queried value or {@code null} * @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/ */
@ -1323,8 +1323,9 @@ public class JSONArray implements Iterable<Object> {
* whitespace is added. If it is not possible to produce a syntactically * whitespace is added. If it is not possible to produce a syntactically
* correct JSON text then null will be returned instead. This could occur if * correct JSON text then null will be returned instead. This could occur if
* the array contains an invalid number. * the array contains an invalid number.
* <p> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @return a printable, displayable, transmittable representation of the * @return a printable, displayable, transmittable representation of the
* array. * array.
@ -1339,8 +1340,23 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Make a pretty-printed JSON text of this JSONArray. Warning: This method * Make a pretty-printed JSON text of this JSONArray.
* assumes that the data structure is acyclical. *
* <p>If <code>indentFactor > 0</code> and the {@link JSONArray} has only
* one element, then the array will be output on a single line:
* <pre>{@code [1]}</pre>
*
* <p>If an array has 2 or more elements, then it will be output across
* multiple lines: <pre>{@code
* [
* 1,
* "value 2",
* 3
* ]
* }</pre>
* <p><b>
* Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @param indentFactor * @param indentFactor
* The number of spaces to add to each level of indentation. * The number of spaces to add to each level of indentation.
@ -1360,8 +1376,9 @@ public class JSONArray implements Iterable<Object> {
/** /**
* Write the contents of the JSONArray as JSON text to a writer. For * Write the contents of the JSONArray as JSON text to a writer. For
* compactness, no whitespace is added. * compactness, no whitespace is added.
* <p> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
*</b>
* *
* @return The writer. * @return The writer.
* @throws JSONException * @throws JSONException
@ -1371,10 +1388,23 @@ public class JSONArray implements Iterable<Object> {
} }
/** /**
* Write the contents of the JSONArray as JSON text to a writer. For * Write the contents of the JSONArray as JSON text to a writer.
* compactness, no whitespace is added. *
* <p> * <p>If <code>indentFactor > 0</code> and the {@link JSONArray} has only
* one element, then the array will be output on a single line:
* <pre>{@code [1]}</pre>
*
* <p>If an array has 2 or more elements, then it will be output across
* multiple lines: <pre>{@code
* [
* 1,
* "value 2",
* 3
* ]
* }</pre>
* <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @param writer * @param writer
* Writes the serialized JSON * Writes the serialized JSON

View file

@ -172,7 +172,7 @@ public class JSONML {
if (!(token instanceof String)) { if (!(token instanceof String)) {
throw x.syntaxError("Missing value"); throw x.syntaxError("Missing value");
} }
newjo.accumulate(attribute, keepStrings ? XML.unescape((String)token) :XML.stringToValue((String)token)); newjo.accumulate(attribute, keepStrings ? ((String)token) :XML.stringToValue((String)token));
token = null; token = null;
} else { } else {
newjo.accumulate(attribute, ""); newjo.accumulate(attribute, "");

View file

@ -1,5 +1,7 @@
package org.json; package org.json;
import java.io.Closeable;
/* /*
Copyright (c) 2002 JSON.org Copyright (c) 2002 JSON.org
@ -28,6 +30,7 @@ import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -228,7 +231,21 @@ public class JSONObject {
if (c != ':') { if (c != ':') {
throw x.syntaxError("Expected a ':' after a key"); throw x.syntaxError("Expected a ':' after a key");
} }
this.putOnce(key, x.nextValue());
// Use syntaxError(..) to include error location
if (key != null) {
// Check if key exists
if (this.opt(key) != null) {
// key already exists
throw x.syntaxError("Duplicate key \"" + key + "\"");
}
// Only add value if non-null
Object value = x.nextValue();
if (value!=null) {
this.put(key, value);
}
}
// Pairs are separated by ','. // Pairs are separated by ','.
@ -276,15 +293,18 @@ public class JSONObject {
* <code>"is"</code> followed by an uppercase letter, the method is invoked, * <code>"is"</code> followed by an uppercase letter, the method is invoked,
* and a key and the value returned from the getter method are put into the * and a key and the value returned from the getter method are put into the
* new JSONObject. * new JSONObject.
* * <p>
* The key is formed by removing the <code>"get"</code> or <code>"is"</code> * The key is formed by removing the <code>"get"</code> or <code>"is"</code>
* 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>
* 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>
* Methods that return <code>void</code> as well as <code>static</code>
* methods are ignored.
* *
* @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
@ -1388,6 +1408,15 @@ public class JSONObject {
return NULL.equals(object) ? defaultValue : object.toString(); return NULL.equals(object) ? defaultValue : object.toString();
} }
/**
* Populates the internal map of the JSONObject with the bean properties.
* The bean can not be recursive.
*
* @see JSONObject#JSONObject(Object)
*
* @param bean
* the bean
*/
private void populateMap(Object bean) { private void populateMap(Object bean) {
Class<?> klass = bean.getClass(); Class<?> klass = bean.getClass();
@ -1397,39 +1426,52 @@ public class JSONObject {
Method[] methods = includeSuperClass ? klass.getMethods() : klass Method[] methods = includeSuperClass ? klass.getMethods() : klass
.getDeclaredMethods(); .getDeclaredMethods();
for (int i = 0; i < methods.length; i += 1) { for (final Method method : methods) {
try { final int modifiers = method.getModifiers();
Method method = methods[i]; if (Modifier.isPublic(modifiers)
if (Modifier.isPublic(method.getModifiers())) { && !Modifier.isStatic(modifiers)
String name = method.getName(); && method.getParameterTypes().length == 0
String key = ""; && !method.isBridge()
if (name.startsWith("get")) { && method.getReturnType() != Void.TYPE ) {
if ("getClass".equals(name) final String name = method.getName();
|| "getDeclaringClass".equals(name)) { String key;
key = ""; if (name.startsWith("get")) {
} else { if ("getClass".equals(name) || "getDeclaringClass".equals(name)) {
key = name.substring(3); continue;
} }
} else if (name.startsWith("is")) { key = name.substring(3);
key = name.substring(2); } 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);
} }
if (key.length() > 0
&& Character.isUpperCase(key.charAt(0))
&& method.getParameterTypes().length == 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);
}
Object result = method.invoke(bean, (Object[]) null); try {
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
// 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 (IllegalArgumentException ignore) {
} catch (InvocationTargetException ignore) {
} }
} }
} catch (Exception ignore) {
} }
} }
} }
@ -1676,7 +1718,7 @@ public class JSONObject {
* Queries and returns a value from this object using {@code jsonPointer}, or * Queries and returns a value from this object using {@code jsonPointer}, or
* returns null if the query fails due to a missing key. * returns null if the query fails due to a missing key.
* *
* @param The JSON pointer * @param jsonPointer The JSON pointer
* @return the queried value or {@code null} * @return the queried value or {@code null}
* @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
*/ */
@ -2004,8 +2046,9 @@ public class JSONObject {
* Make a JSON text of this JSONObject. For compactness, no whitespace is * Make a JSON text of this JSONObject. For compactness, no whitespace is
* added. If this would not result in a syntactically correct JSON text, * added. If this would not result in a syntactically correct JSON text,
* then null will be returned instead. * then null will be returned instead.
* <p> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @return a printable, displayable, portable, transmittable representation * @return a printable, displayable, portable, transmittable representation
* of the object, beginning with <code>{</code>&nbsp;<small>(left * of the object, beginning with <code>{</code>&nbsp;<small>(left
@ -2023,8 +2066,20 @@ public class JSONObject {
/** /**
* Make a pretty-printed JSON text of this JSONObject. * Make a pretty-printed JSON text of this JSONObject.
* <p> *
* <p>If <code>indentFactor > 0</code> and the {@link JSONObject}
* has only one key, then the object will be output on a single line:
* <pre>{@code {"key": 1}}</pre>
*
* <p>If an object has 2 or more keys, then it will be output across
* multiple lines: <code><pre>{
* "key1": 1,
* "key2": "value 2",
* "key3": 3
* }</pre></code>
* <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @param indentFactor * @param indentFactor
* The number of spaces to add to each level of indentation. * The number of spaces to add to each level of indentation.
@ -2130,8 +2185,9 @@ public class JSONObject {
/** /**
* Write the contents of the JSONObject as JSON text to a writer. For * Write the contents of the JSONObject as JSON text to a writer. For
* compactness, no whitespace is added. * compactness, no whitespace is added.
* <p> * <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @return The writer. * @return The writer.
* @throws JSONException * @throws JSONException
@ -2196,8 +2252,20 @@ public class JSONObject {
/** /**
* Write the contents of the JSONObject as JSON text to a writer. * Write the contents of the JSONObject as JSON text to a writer.
* <p> *
* <p>If <code>indentFactor > 0</code> and the {@link JSONObject}
* has only one key, then the object will be output on a single line:
* <pre>{@code {"key": 1}}</pre>
*
* <p>If an object has 2 or more keys, then it will be output across
* multiple lines: <code><pre>{
* "key1": 1,
* "key2": "value 2",
* "key3": 3
* }</pre></code>
* <p><b>
* Warning: This method assumes that the data structure is acyclical. * Warning: This method assumes that the data structure is acyclical.
* </b>
* *
* @param writer * @param writer
* Writes the serialized JSON * Writes the serialized JSON

2
README
View file

@ -89,6 +89,8 @@ invalid number formats (1.2e6.3) will cause errors as such documents can not be
reliably. reliably.
Release history: Release history:
20171018 Checkpoint for recent commits.
20170516 Roll up recent commits. 20170516 Roll up recent commits.
20160810 Revert code that was breaking opt*() methods. 20160810 Revert code that was breaking opt*() methods.

View file

@ -140,7 +140,7 @@ public class XML {
if (mustEscape(cp)) { if (mustEscape(cp)) {
sb.append("&#x"); sb.append("&#x");
sb.append(Integer.toHexString(cp)); sb.append(Integer.toHexString(cp));
sb.append(";"); sb.append(';');
} else { } else {
sb.appendCodePoint(cp); sb.appendCodePoint(cp);
} }
@ -190,31 +190,7 @@ public class XML {
final int semic = string.indexOf(';', i); final int semic = string.indexOf(';', i);
if (semic > i) { if (semic > i) {
final String entity = string.substring(i + 1, semic); final String entity = string.substring(i + 1, semic);
if (entity.charAt(0) == '#') { sb.append(XMLTokener.unescapeEntity(entity));
int cp;
if (entity.charAt(1) == 'x') {
// hex encoded unicode
cp = Integer.parseInt(entity.substring(2), 16);
} else {
// decimal encoded unicode
cp = Integer.parseInt(entity.substring(1));
}
sb.appendCodePoint(cp);
} else {
if ("quot".equalsIgnoreCase(entity)) {
sb.append('"');
} else if ("amp".equalsIgnoreCase(entity)) {
sb.append('&');
} else if ("apos".equalsIgnoreCase(entity)) {
sb.append('\'');
} else if ("lt".equalsIgnoreCase(entity)) {
sb.append('<');
} else if ("gt".equalsIgnoreCase(entity)) {
sb.append('>');
} else {// unsupported xml entity. leave encoded
sb.append('&').append(entity).append(';');
}
}
// skip past the entity we just parsed. // skip past the entity we just parsed.
i += entity.length() + 1; i += entity.length() + 1;
} else { } else {
@ -363,7 +339,7 @@ public class XML {
throw x.syntaxError("Missing value"); throw x.syntaxError("Missing value");
} }
jsonobject.accumulate(string, jsonobject.accumulate(string,
keepStrings ? unescape((String)token) : stringToValue((String) token)); keepStrings ? ((String)token) : stringToValue((String) token));
token = null; token = null;
} else { } else {
jsonobject.accumulate(string, ""); jsonobject.accumulate(string, "");
@ -395,7 +371,7 @@ public class XML {
string = (String) token; string = (String) token;
if (string.length() > 0) { if (string.length() > 0) {
jsonobject.accumulate("content", jsonobject.accumulate("content",
keepStrings ? unescape(string) : stringToValue(string)); keepStrings ? string : stringToValue(string));
} }
} else if (token == LT) { } else if (token == LT) {
@ -422,14 +398,15 @@ 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) {
if (string.equals("")) { if (string.equals("")) {
return string; return string;
} }
if (string.equalsIgnoreCase("true")) { if (string.equalsIgnoreCase("true")) {
@ -470,8 +447,7 @@ public class XML {
} catch (Exception ignore) { } catch (Exception ignore) {
} }
} }
return string;
return unescape(string);
} }
/** /**

View file

@ -138,8 +138,37 @@ public class XMLTokener extends JSONTokener {
} }
} }
String string = sb.toString(); String string = sb.toString();
Object object = entity.get(string); return unescapeEntity(string);
return object != null ? object : ampersand + string + ";"; }
/**
* Unescapes an XML entity encoding;
* @param e entity (only the actual entity value, not the preceding & or ending ;
* @return
*/
static String unescapeEntity(String e) {
// validate
if (e == null || e.isEmpty()) {
return "";
}
// if our entity is an encoded unicode point, parse it.
if (e.charAt(0) == '#') {
int cp;
if (e.charAt(1) == 'x') {
// hex encoded unicode
cp = Integer.parseInt(e.substring(2), 16);
} else {
// decimal encoded unicode
cp = Integer.parseInt(e.substring(1));
}
return new String(new int[] {cp},0,1);
}
Character knownEntity = entity.get(e);
if(knownEntity==null) {
// we don't know the entity so keep it encoded
return '&' + e + ';';
}
return knownEntity.toString();
} }