From e3839d669c9dab07b395afafad5b16a3f30449cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20G=C3=B6ransson?= Date: Wed, 20 Mar 2013 10:25:49 +0100 Subject: [PATCH 1/3] Fixed issue #1660 & #1680 Changed the access modifier for the constructor JSONArray(JSONTokener) to protected so that the other json classes can access it. Removed the extra JSONTokener inner class in JSONObject, this caused classes outside of JSONObject to use the wrong constructor JSONObject(Object bean) instead --- core/src/processing/data/JSONArray.java | 2 +- core/src/processing/data/JSONObject.java | 750 +++++++++++------------ 2 files changed, 376 insertions(+), 376 deletions(-) diff --git a/core/src/processing/data/JSONArray.java b/core/src/processing/data/JSONArray.java index 8f9c17ce82..9c6a48a5b6 100644 --- a/core/src/processing/data/JSONArray.java +++ b/core/src/processing/data/JSONArray.java @@ -107,7 +107,7 @@ public JSONArray() { * @param x A JSONTokener * @throws JSONException If there is a syntax error. */ - private JSONArray(JSONTokener x) { + /*private*/protected JSONArray(JSONTokener x) { this(); if (x.nextClean() != '[') { throw new RuntimeException("A JSONArray text must start with '['"); diff --git a/core/src/processing/data/JSONObject.java b/core/src/processing/data/JSONObject.java index 4f9980b085..2257fa9226 100644 --- a/core/src/processing/data/JSONObject.java +++ b/core/src/processing/data/JSONObject.java @@ -1715,379 +1715,379 @@ protected Writer write(Writer writer, int indentFactor, int indent) { // } - static class JSONTokener { - private long character; - private boolean eof; - private long index; - private long line; - private char previous; - private Reader reader; - private boolean usePrevious; - - - /** - * Construct a JSONTokener from a Reader. - * - * @param reader A reader. - */ - public JSONTokener(Reader reader) { - this.reader = reader.markSupported() - ? reader - : new BufferedReader(reader); - this.eof = false; - this.usePrevious = false; - this.previous = 0; - this.index = 0; - this.character = 1; - this.line = 1; - } - - - /** - * Construct a JSONTokener from an InputStream. - */ - public JSONTokener(InputStream inputStream) { - this(new InputStreamReader(inputStream)); - } - - - /** - * Construct a JSONTokener from a string. - * - * @param s A source string. - */ - public JSONTokener(String s) { - this(new StringReader(s)); - } - - - /** - * Back up one character. This provides a sort of lookahead capability, - * so that you can test for a digit or letter before attempting to parse - * the next number or identifier. - */ - public void back() { - if (this.usePrevious || this.index <= 0) { - throw new RuntimeException("Stepping back two steps is not supported"); - } - this.index -= 1; - this.character -= 1; - this.usePrevious = true; - this.eof = false; - } - - - public boolean end() { - return this.eof && !this.usePrevious; - } - - - /** - * Determine if the source string still contains characters that next() - * can consume. - * @return true if not yet at the end of the source. - */ - public boolean more() { - this.next(); - if (this.end()) { - return false; - } - this.back(); - return true; - } - - - /** - * Get the next character in the source string. - * - * @return The next character, or 0 if past the end of the source string. - */ - public char next() { - int c; - if (this.usePrevious) { - this.usePrevious = false; - c = this.previous; - } else { - try { - c = this.reader.read(); - } catch (IOException exception) { - throw new RuntimeException(exception); - } - - if (c <= 0) { // End of stream - this.eof = true; - c = 0; - } - } - this.index += 1; - if (this.previous == '\r') { - this.line += 1; - this.character = c == '\n' ? 0 : 1; - } else if (c == '\n') { - this.line += 1; - this.character = 0; - } else { - this.character += 1; - } - this.previous = (char) c; - return this.previous; - } - - - /** - * Consume the next character, and check that it matches a specified - * character. - * @param c The character to match. - * @return The character. - * @throws JSONException if the character does not match. - */ - public char next(char c) { - char n = this.next(); - if (n != c) { - throw new RuntimeException("Expected '" + c + "' and instead saw '" + n + "'"); - } - return n; - } - - - /** - * Get the next n characters. - * - * @param n The number of characters to take. - * @return A string of n characters. - * @throws JSONException - * Substring bounds error if there are not - * n characters remaining in the source string. - */ - public String next(int n) { - if (n == 0) { - return ""; - } - - char[] chars = new char[n]; - int pos = 0; - - while (pos < n) { - chars[pos] = this.next(); - if (this.end()) { - throw new RuntimeException("Substring bounds error"); - } - pos += 1; - } - return new String(chars); - } - - - /** - * Get the next char in the string, skipping whitespace. - * @throws JSONException - * @return A character, or 0 if there are no more characters. - */ - public char nextClean() { - for (;;) { - char c = this.next(); - if (c == 0 || c > ' ') { - return c; - } - } - } - - - /** - * Return the characters up to the next close quote character. - * Backslash processing is done. The formal JSON format does not - * allow strings in single quotes, but an implementation is allowed to - * accept them. - * @param quote The quoting character, either - * " (double quote) or - * ' (single quote). - * @return A String. - * @throws JSONException Unterminated string. - */ - public String nextString(char quote) { - char c; - StringBuffer sb = new StringBuffer(); - for (;;) { - c = this.next(); - switch (c) { - case 0: - case '\n': - case '\r': - throw new RuntimeException("Unterminated string"); - case '\\': - c = this.next(); - switch (c) { - case 'b': - sb.append('\b'); - break; - case 't': - sb.append('\t'); - break; - case 'n': - sb.append('\n'); - break; - case 'f': - sb.append('\f'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - sb.append((char)Integer.parseInt(this.next(4), 16)); - break; - case '"': - case '\'': - case '\\': - case '/': - sb.append(c); - break; - default: - throw new RuntimeException("Illegal escape."); - } - break; - default: - if (c == quote) { - return sb.toString(); - } - sb.append(c); - } - } - } - - - /** - * Get the text up but not including the specified character or the - * end of line, whichever comes first. - * @param delimiter A delimiter character. - * @return A string. - */ - public String nextTo(char delimiter) { - StringBuffer sb = new StringBuffer(); - for (;;) { - char c = this.next(); - if (c == delimiter || c == 0 || c == '\n' || c == '\r') { - if (c != 0) { - this.back(); - } - return sb.toString().trim(); - } - sb.append(c); - } - } - - - /** - * Get the text up but not including one of the specified delimiter - * characters or the end of line, whichever comes first. - * @param delimiters A set of delimiter characters. - * @return A string, trimmed. - */ - public String nextTo(String delimiters) { - char c; - StringBuffer sb = new StringBuffer(); - for (;;) { - c = this.next(); - if (delimiters.indexOf(c) >= 0 || c == 0 || - c == '\n' || c == '\r') { - if (c != 0) { - this.back(); - } - return sb.toString().trim(); - } - sb.append(c); - } - } - - - /** - * Get the next value. The value can be a Boolean, Double, Integer, - * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. - * @throws JSONException If syntax error. - * - * @return An object. - */ - public Object nextValue() { - char c = this.nextClean(); - String string; - - switch (c) { - case '"': - case '\'': - return this.nextString(c); - case '{': - this.back(); - return new JSONObject(this); - case '[': - this.back(); - return new JSONArray(this); - } - - /* - * Handle unquoted text. This could be the values true, false, or - * null, or it can be a number. An implementation (such as this one) - * is allowed to also accept non-standard forms. - * - * Accumulate characters until we reach the end of the text or a - * formatting character. - */ - - StringBuffer sb = new StringBuffer(); - while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { - sb.append(c); - c = this.next(); - } - this.back(); - - string = sb.toString().trim(); - if ("".equals(string)) { - throw new RuntimeException("Missing value"); - } - return JSONObject.stringToValue(string); - } - - - /** - * Skip characters until the next character is the requested character. - * If the requested character is not found, no characters are skipped. - * @param to A character to skip to. - * @return The requested character, or zero if the requested character - * is not found. - */ - public char skipTo(char to) { - char c; - try { - long startIndex = this.index; - long startCharacter = this.character; - long startLine = this.line; - this.reader.mark(1000000); - do { - c = this.next(); - if (c == 0) { - this.reader.reset(); - this.index = startIndex; - this.character = startCharacter; - this.line = startLine; - return c; - } - } while (c != to); - } catch (IOException exc) { - throw new RuntimeException(exc); - } - - this.back(); - return c; - } - - - /** - * Make a printable string of this JSONTokener. - * - * @return " at {index} [character {character} line {line}]" - */ - @Override - public String toString() { - return " at " + this.index + " [character " + this.character + " line " + - this.line + "]"; - } - } +// static class JSONTokener { +// private long character; +// private boolean eof; +// private long index; +// private long line; +// private char previous; +// private Reader reader; +// private boolean usePrevious; +// +// +// /** +// * Construct a JSONTokener from a Reader. +// * +// * @param reader A reader. +// */ +// public JSONTokener(Reader reader) { +// this.reader = reader.markSupported() +// ? reader +// : new BufferedReader(reader); +// this.eof = false; +// this.usePrevious = false; +// this.previous = 0; +// this.index = 0; +// this.character = 1; +// this.line = 1; +// } +// +// +// /** +// * Construct a JSONTokener from an InputStream. +// */ +// public JSONTokener(InputStream inputStream) { +// this(new InputStreamReader(inputStream)); +// } +// +// +// /** +// * Construct a JSONTokener from a string. +// * +// * @param s A source string. +// */ +// public JSONTokener(String s) { +// this(new StringReader(s)); +// } +// +// +// /** +// * Back up one character. This provides a sort of lookahead capability, +// * so that you can test for a digit or letter before attempting to parse +// * the next number or identifier. +// */ +// public void back() { +// if (this.usePrevious || this.index <= 0) { +// throw new RuntimeException("Stepping back two steps is not supported"); +// } +// this.index -= 1; +// this.character -= 1; +// this.usePrevious = true; +// this.eof = false; +// } +// +// +// public boolean end() { +// return this.eof && !this.usePrevious; +// } +// +// +// /** +// * Determine if the source string still contains characters that next() +// * can consume. +// * @return true if not yet at the end of the source. +// */ +// public boolean more() { +// this.next(); +// if (this.end()) { +// return false; +// } +// this.back(); +// return true; +// } +// +// +// /** +// * Get the next character in the source string. +// * +// * @return The next character, or 0 if past the end of the source string. +// */ +// public char next() { +// int c; +// if (this.usePrevious) { +// this.usePrevious = false; +// c = this.previous; +// } else { +// try { +// c = this.reader.read(); +// } catch (IOException exception) { +// throw new RuntimeException(exception); +// } +// +// if (c <= 0) { // End of stream +// this.eof = true; +// c = 0; +// } +// } +// this.index += 1; +// if (this.previous == '\r') { +// this.line += 1; +// this.character = c == '\n' ? 0 : 1; +// } else if (c == '\n') { +// this.line += 1; +// this.character = 0; +// } else { +// this.character += 1; +// } +// this.previous = (char) c; +// return this.previous; +// } +// +// +// /** +// * Consume the next character, and check that it matches a specified +// * character. +// * @param c The character to match. +// * @return The character. +// * @throws JSONException if the character does not match. +// */ +// public char next(char c) { +// char n = this.next(); +// if (n != c) { +// throw new RuntimeException("Expected '" + c + "' and instead saw '" + n + "'"); +// } +// return n; +// } +// +// +// /** +// * Get the next n characters. +// * +// * @param n The number of characters to take. +// * @return A string of n characters. +// * @throws JSONException +// * Substring bounds error if there are not +// * n characters remaining in the source string. +// */ +// public String next(int n) { +// if (n == 0) { +// return ""; +// } +// +// char[] chars = new char[n]; +// int pos = 0; +// +// while (pos < n) { +// chars[pos] = this.next(); +// if (this.end()) { +// throw new RuntimeException("Substring bounds error"); +// } +// pos += 1; +// } +// return new String(chars); +// } +// +// +// /** +// * Get the next char in the string, skipping whitespace. +// * @throws JSONException +// * @return A character, or 0 if there are no more characters. +// */ +// public char nextClean() { +// for (;;) { +// char c = this.next(); +// if (c == 0 || c > ' ') { +// return c; +// } +// } +// } +// +// +// /** +// * Return the characters up to the next close quote character. +// * Backslash processing is done. The formal JSON format does not +// * allow strings in single quotes, but an implementation is allowed to +// * accept them. +// * @param quote The quoting character, either +// * " (double quote) or +// * ' (single quote). +// * @return A String. +// * @throws JSONException Unterminated string. +// */ +// public String nextString(char quote) { +// char c; +// StringBuffer sb = new StringBuffer(); +// for (;;) { +// c = this.next(); +// switch (c) { +// case 0: +// case '\n': +// case '\r': +// throw new RuntimeException("Unterminated string"); +// case '\\': +// c = this.next(); +// switch (c) { +// case 'b': +// sb.append('\b'); +// break; +// case 't': +// sb.append('\t'); +// break; +// case 'n': +// sb.append('\n'); +// break; +// case 'f': +// sb.append('\f'); +// break; +// case 'r': +// sb.append('\r'); +// break; +// case 'u': +// sb.append((char)Integer.parseInt(this.next(4), 16)); +// break; +// case '"': +// case '\'': +// case '\\': +// case '/': +// sb.append(c); +// break; +// default: +// throw new RuntimeException("Illegal escape."); +// } +// break; +// default: +// if (c == quote) { +// return sb.toString(); +// } +// sb.append(c); +// } +// } +// } +// +// +// /** +// * Get the text up but not including the specified character or the +// * end of line, whichever comes first. +// * @param delimiter A delimiter character. +// * @return A string. +// */ +// public String nextTo(char delimiter) { +// StringBuffer sb = new StringBuffer(); +// for (;;) { +// char c = this.next(); +// if (c == delimiter || c == 0 || c == '\n' || c == '\r') { +// if (c != 0) { +// this.back(); +// } +// return sb.toString().trim(); +// } +// sb.append(c); +// } +// } +// +// +// /** +// * Get the text up but not including one of the specified delimiter +// * characters or the end of line, whichever comes first. +// * @param delimiters A set of delimiter characters. +// * @return A string, trimmed. +// */ +// public String nextTo(String delimiters) { +// char c; +// StringBuffer sb = new StringBuffer(); +// for (;;) { +// c = this.next(); +// if (delimiters.indexOf(c) >= 0 || c == 0 || +// c == '\n' || c == '\r') { +// if (c != 0) { +// this.back(); +// } +// return sb.toString().trim(); +// } +// sb.append(c); +// } +// } +// +// +// /** +// * Get the next value. The value can be a Boolean, Double, Integer, +// * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. +// * @throws JSONException If syntax error. +// * +// * @return An object. +// */ +// public Object nextValue() { +// char c = this.nextClean(); +// String string; +// +// switch (c) { +// case '"': +// case '\'': +// return this.nextString(c); +// case '{': +// this.back(); +// return new JSONObject(this); +// case '[': +// this.back(); +// return new JSONArray(this); +// } +// +// /* +// * Handle unquoted text. This could be the values true, false, or +// * null, or it can be a number. An implementation (such as this one) +// * is allowed to also accept non-standard forms. +// * +// * Accumulate characters until we reach the end of the text or a +// * formatting character. +// */ +// +// StringBuffer sb = new StringBuffer(); +// while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { +// sb.append(c); +// c = this.next(); +// } +// this.back(); +// +// string = sb.toString().trim(); +// if ("".equals(string)) { +// throw new RuntimeException("Missing value"); +// } +// return JSONObject.stringToValue(string); +// } +// +// +// /** +// * Skip characters until the next character is the requested character. +// * If the requested character is not found, no characters are skipped. +// * @param to A character to skip to. +// * @return The requested character, or zero if the requested character +// * is not found. +// */ +// public char skipTo(char to) { +// char c; +// try { +// long startIndex = this.index; +// long startCharacter = this.character; +// long startLine = this.line; +// this.reader.mark(1000000); +// do { +// c = this.next(); +// if (c == 0) { +// this.reader.reset(); +// this.index = startIndex; +// this.character = startCharacter; +// this.line = startLine; +// return c; +// } +// } while (c != to); +// } catch (IOException exc) { +// throw new RuntimeException(exc); +// } +// +// this.back(); +// return c; +// } +// +// +// /** +// * Make a printable string of this JSONTokener. +// * +// * @return " at {index} [character {character} line {line}]" +// */ +// @Override +// public String toString() { +// return " at " + this.index + " [character " + this.character + " line " + +// this.line + "]"; +// } +// } } From 659a3c77a4870c18a2295041bad93b380ce5a989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20G=C3=B6ransson?= Date: Wed, 20 Mar 2013 15:57:22 +0100 Subject: [PATCH 2/3] Fixed method setJSONObject() and setJSONArray() It now accepts a JSONObject (or JSONArray) as parameter instead of a simple string. Error: The method setJSONObject(String, String) in the type JSONObject is not applicable for the arguments (String, JSONObject) The method setJSONArray(String, String) in the type JSONObject is not applicable for the arguments (String, JSONArray) --- core/src/processing/data/JSONObject.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/processing/data/JSONObject.java b/core/src/processing/data/JSONObject.java index 2257fa9226..5dbf4abcf4 100644 --- a/core/src/processing/data/JSONObject.java +++ b/core/src/processing/data/JSONObject.java @@ -1120,12 +1120,12 @@ public JSONObject setBoolean(String key, boolean value) { } - public JSONObject setJSONObject(String key, String value) { + public JSONObject setJSONObject(String key, JSONObject/*String*/ value) { return put(key, value); } - public JSONObject setJSONArray(String key, String value) { + public JSONObject setJSONArray(String key, JSONArray/*String*/ value) { return put(key, value); } From 910b30d8a6d920e6978ebe997f3e59f94fbedb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20G=C3=B6ransson?= Date: Wed, 20 Mar 2013 15:59:56 +0100 Subject: [PATCH 3/3] Updated example LoadSaveJSON Fixed the JSON example. I left the data.json formatted as it was, however it is not as "sleek" as it could be. The json could start with the array straight away and that way cut away alot of the unnecessary code required to maintain the format through load and save. --- .../LoadSaveJSON/LoadSaveJSON.pde | 155 +++++++++--------- 1 file changed, 78 insertions(+), 77 deletions(-) diff --git a/java/examples/Topics/Advanced Data/LoadSaveJSON/LoadSaveJSON.pde b/java/examples/Topics/Advanced Data/LoadSaveJSON/LoadSaveJSON.pde index 06f6e20d2c..e34843ff29 100644 --- a/java/examples/Topics/Advanced Data/LoadSaveJSON/LoadSaveJSON.pde +++ b/java/examples/Topics/Advanced Data/LoadSaveJSON/LoadSaveJSON.pde @@ -1,5 +1,5 @@ /** - * Loading XML Data + * Loading JSON Data * by Daniel Shiffman. * * This example demonstrates how to use loadJSON() @@ -31,7 +31,7 @@ } } */ - + // An Array of Bubble objects Bubble[] bubbles; // A Table object @@ -45,88 +45,89 @@ void setup() { void draw() { background(255); // Display all bubbles -// for (Bubble b : bubbles) { -// b.display(); -// b.rollover(mouseX, mouseY); -// } -// -// textAlign(LEFT); -// fill(0); -// text("Click to add bubbles.", 10, height-10); + for (Bubble b : bubbles) { + b.display(); + b.rollover(mouseX, mouseY); + } + + textAlign(LEFT); + fill(0); + text("Click to add bubbles.", 10, height-10); } void loadData() { // Load JSON file - String jsonString = join(loadStrings("data.json"),"\n"); + String jsonString = join(loadStrings("data.json"), "\n"); //println(jsonString); - - json = JSONArray.parse(jsonString); - println(json); - - // Get all the child nodes named "bubble" -// XML[] children = xml.getChildren("bubble"); -// -// // The size of the array of Bubble objects is determined by the total XML elements named "bubble" -// bubbles = new Bubble[children.length]; -// -// for (int i = 0; i < bubbles.length; i++) { -// -// // The position element has two attributes: x and y -// XML positionElement = children[i].getChild("position"); -// // Note how with attributes we can get an integer or float directly -// float x = positionElement.getInt("x"); -// float y = positionElement.getInt("y"); -// -// // The diameter is the content of the child named "diamater" -// XML diameterElement = children[i].getChild("diameter"); -// // Note how with the content of an XML node, we retrieve as a String and then convert -// float diameter = float(diameterElement.getContent()); -// -// // The label is the content of the child named "label" -// XML labelElement = children[i].getChild("label"); -// String label = labelElement.getContent(); -// -// // Make a Bubble object out of the data read -// bubbles[i] = new Bubble(x, y, diameter, label); -// } -} + // Load the entire document in a JSONObject + JSONObject root = JSONObject.parse(jsonString); -// Still need to work on adding and deleting + // Fetch the internal JSONObject called "bubbles" (all the bubbles are stored inside this object) + JSONObject json_bubbles = root.getJSONObject("bubbles"); -void mousePressed() { - - // Create a new XML bubble element -// XML bubble = xml.addChild("bubble"); -// -// // Set the poisition element -// XML position = bubble.addChild("position"); -// // Here we can set attributes as integers directly -// position.setInt("x",mouseX); -// position.setInt("y",mouseY); -// -// // Set the diameter element -// XML diameter = bubble.addChild("diameter"); -// // Here for a node's content, we have to convert to a String -// diameter.setContent("" + random(40,80)); -// -// // Set a label -// XML label = bubble.addChild("label"); -// label.setContent("New label"); -// -// -// // Here we are removing the oldest bubble if there are more than 10 -// XML[] children = xml.getChildren("bubble"); -// // If the XML file has more than 10 bubble elements -// if (children.length > 10) { -// // Delete the first one -// xml.removeChild(children[0]); -// } -// -// // Save a new XML file -// saveXML(xml,"data/data.xml"); -// -// // reload the new data -// loadData(); + // Load the JSONArray of bubbles + json = json_bubbles.getJSONArray("bubble"); + + // The size of the array of Bubble objects is determined by the total XML elements named "bubble" + bubbles = new Bubble[json.size()]; + + for (int i = 0; i < bubbles.length; i++) { + + // Get the JSONObject representing the bubble + JSONObject bubble = json.getObject(i); + + // The position element has two attributes: x and y + float x = bubble.getJSONObject("position").getInt("x"); + float y = bubble.getJSONObject("position").getInt("y"); + + // The diameter is the content of the child named "diamater" + float diameter = bubble.getFloat("diameter"); + + // The label is the content of the child named "label" + String label = bubble.getString("label"); + + // Make a Bubble object out of the data read + bubbles[i] = new Bubble(x, y, diameter, label); + } } +void mousePressed() { + + // Create a new JSON bubble element + JSONObject newBubble = new JSONObject(); + + // Set the poisition element + JSONObject position = new JSONObject(); + position.setInt("x", mouseX); + position.setInt("y", mouseY); + newBubble.setJSONObject("position", position); + + // Set the diameter element + newBubble.setFloat("diameter", random(40, 80)); + + // Set a label + newBubble.setString("label", "New label"); + + // Append the new bubble to the JSONArray json + json.append( newBubble ); + + // Here we are removing the oldest bubble if there are more than 10 + // If the XML file has more than 10 bubble elements + if (json.size() > 10) { + // Delete the first one + json.removeIndex(0); + } + + JSONObject jsonBubbles = new JSONObject(); + jsonBubbles.setJSONArray( "bubble", json ); + + JSONObject root = new JSONObject(); + root.setJSONObject( "bubbles", jsonBubbles ); + + // Save a new XML file + saveStrings("data/data.json", split(root.toString(), "\n")); + + // reload the new data + loadData(); +}