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..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); } @@ -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 + "]"; +// } +// } } 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(); +}