From 31069979332a418527d4e64c5a188910fb6d2876 Mon Sep 17 00:00:00 2001 From: ColinTree <502470184@qq.com> Date: Sun, 2 Sep 2018 17:32:28 +0800 Subject: [PATCH] Version 2: Added support of Parsing Json String / List & added operations on JsonArray / JsonObject --- JsonArray.java | 77 +++++++++++++ JsonObject.java | 101 +++++++++++++++++ JsonType.java | 18 ++++ JsonUtils.java | 281 ++++++++++++++++++++++++++++++++++++++++-------- README.md | 132 ++++++++++++++++++++++- 5 files changed, 559 insertions(+), 50 deletions(-) create mode 100644 JsonArray.java create mode 100644 JsonObject.java create mode 100644 JsonType.java diff --git a/JsonArray.java b/JsonArray.java new file mode 100644 index 0000000..870160d --- /dev/null +++ b/JsonArray.java @@ -0,0 +1,77 @@ +package cn.colintree.aix.JsonUtils; + +import com.google.appinventor.components.runtime.ComponentContainer; +import com.google.appinventor.components.runtime.util.YailList; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * A non visible container for JSONArray + * + */ +public final class JsonArray extends JsonType { + + public static JsonArray parseJsonArray(YailList list) throws JSONException { + return new JsonArray(parseJSONArray(list)); + } + + public static JSONArray parseJSONArray(YailList list) throws JSONException { + if (JsonObject.canParseJSONObject(list)) { + throw new IllegalArgumentException(); + } + int size = list.size(); + JSONArray array = new JSONArray(); + Object item; + for (int i = 0; i < size; i++) { + item = list.getObject(i); + array.put(item instanceof YailList + ? JsonUtils.List2Json((YailList) item) + : item); + } + return array; + } + + private final JSONArray array; + + JsonArray(ComponentContainer container) { + this(new JSONArray()); + } + JsonArray(JSONArray array) { + super(null); + if (array == null) { + array = new JSONArray(); + } + this.array = array; + } + + @Override + public String toString() { + return array.toString(); + } + + public JSONArray getArray() { + return array; + } + + @Override + public YailList toList() { + return toList(array); + } + + public static YailList toList(JSONArray array) { + int length = array.length(); + Object[] objs = new Object[length]; + for (int i = 0; i < length; i++) { + objs[i] = array.opt(i); + if (objs[i] instanceof JSONArray) { + objs[i] = JsonArray.toList((JSONArray) objs[i]); + } else if (objs[i] instanceof JSONObject) { + objs[i] = JsonObject.toList((JSONObject) objs[i]); + } + } + return YailList.makeList(objs); + } + +} \ No newline at end of file diff --git a/JsonObject.java b/JsonObject.java new file mode 100644 index 0000000..b0a0cd3 --- /dev/null +++ b/JsonObject.java @@ -0,0 +1,101 @@ +package cn.colintree.aix.JsonUtils; + +import java.util.Iterator; + +import com.google.appinventor.components.runtime.ComponentContainer; +import com.google.appinventor.components.runtime.util.YailList; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * A non visible container for JSONObject + */ +public final class JsonObject extends JsonType { + + public static JsonObject parseJsonObject(YailList list) throws JSONException { + return new JsonObject(parseJSONObject(list)); + } + + public static JSONObject parseJSONObject(YailList list) throws JSONException { + if (!canParseJSONObject(list)) { + throw new IllegalArgumentException(); + } + int size = list.size(); + JSONObject object = new JSONObject(); + YailList itemList; + for (int i = 0; i < size; i++) { + itemList = (YailList) list.getObject(i); + object.put( + (String) itemList.getObject(0), + itemList.getObject(1) instanceof YailList + ? JsonUtils.List2Json((YailList) itemList.getObject(1)) + : itemList.getObject(1) + ); + } + return object; + } + + public static boolean canParseJSONObject(YailList list) { + int size = list.size(); + Object item; + for (int i = 0; i < size; i++) { + item = list.getObject(i); + if (item instanceof YailList && ((YailList)item).size() == 2 + && ((YailList)item).getObject(0) instanceof String) { + // this item is a valid object item + } else { + return false; + } + } + return true; + } + + + private final JSONObject object; + + JsonObject(ComponentContainer container) { + this(new JSONObject()); + } + JsonObject(JSONObject object) { + super(null); + this.object = object; + } + + @Override + public String toString() { + return object.toString(); + } + + public JSONObject getObject() { + return object; + } + + @Override + public YailList toList() { + return toList(object); + } + + public static YailList toList(JSONObject object) { + int length = object.length(); + Object[] objs = new Object[length]; + int i = 0; + String key; + Object value; + Iterator iterator = object.keys(); + while (iterator.hasNext()) { + key = iterator.next(); + value = object.opt(key); + if (value instanceof JSONArray) { + value = JsonArray.toList((JSONArray) value); + } else if (value instanceof JSONObject) { + value = JsonObject.toList((JSONObject) value); + } + objs[i] = YailList.makeList(new Object[]{ key, value }); + i++; + } + return YailList.makeList(objs); + } + +} \ No newline at end of file diff --git a/JsonType.java b/JsonType.java new file mode 100644 index 0000000..be5169b --- /dev/null +++ b/JsonType.java @@ -0,0 +1,18 @@ +package cn.colintree.aix.JsonUtils; + +import com.google.appinventor.components.runtime.AndroidNonvisibleComponent; +import com.google.appinventor.components.runtime.ComponentContainer; +import com.google.appinventor.components.runtime.util.YailList; + +public abstract class JsonType extends AndroidNonvisibleComponent { + + public JsonType(ComponentContainer container) { + super(null); + } + + @Override + public abstract String toString(); + + public abstract YailList toList(); + +} \ No newline at end of file diff --git a/JsonUtils.java b/JsonUtils.java index eae7336..0fb6793 100644 --- a/JsonUtils.java +++ b/JsonUtils.java @@ -15,73 +15,262 @@ @SimpleObject(external = true) @DesignerComponent( category = ComponentCategory.EXTENSION, + description = "", iconName = "images/extension.png", nonVisible = true, helpUrl = "https://github.com/OpenSourceAIX/JsonUtils", - version = 1) + version = JsonUtils.VERSION) public class JsonUtils extends AndroidNonvisibleComponent { - + + public static final int VERSION = 2; + public JsonUtils(ComponentContainer container) { super(container.$form()); } @SimpleFunction( - description = "Convert list into an json string, " - + "for more details see https://github.com/OpenSourceAIX/JsonUtils") - public static String List2JsonString(YailList list) throws JSONException { + description = "Convert list into an json string. Would " + + "For more details about accepted format, " + + "see https://github.com/OpenSourceAIX/JsonUtils") + public static String List2JsonString(YailList list) { return List2Json(list).toString(); } - public static Object List2Json(YailList list) throws JSONException { - if (list == null) { - throw new IllegalArgumentException(); - } - if (list.size()==0) { + + static Object List2Json(YailList list) { + if (list == null || list.size() == 0) { return new JSONArray(); } - int size = list.size(); - if (isJsonObject(list)) { - JSONObject object = new JSONObject(); - YailList itemList; - for (int i = 0; i < size; i++) { - itemList = (YailList) list.getObject(i); - object.put( - (String) itemList.getObject(0), - itemList.getObject(1) instanceof YailList - ? List2Json((YailList) itemList.getObject(1)) - : itemList.getObject(1) - ); + try { + // using try-catch can reduce times to call JsonObject.CanParseJSONObject() + try { + return JsonArray.parseJSONArray(list); + } catch (IllegalArgumentException ok) { + return JsonObject.parseJSONObject(list); } - return object; - } else { - JSONArray array = new JSONArray(); - Object item; - for (int i = 0; i < size; i++) { - item = list.getObject(i); - array.put(item instanceof YailList - ? List2Json((YailList) item) - : item); - } - return array; + } catch (JSONException e) { + return new JSONArray(); } } /** - * Check if the List can be converted into a JsonObject + * Check if the List can be converted into a JSONObject */ + @Deprecated @SimpleFunction( - description = "Check if the List can be converted into a JsonObject") + description = "Check if the List can be converted into a JsonObject. \n" + + "THIS METHOD IS DEPRECATED, PLEASE USE CanParseJsonObject() INSTEAD!", + userVisible = false) public static boolean isJsonObject(YailList list) { - int size = list.size(); - Object item; - for (int i = 0; i < size; i++) { - item = list.getObject(i); - if (item instanceof YailList && ((YailList)item).size() == 2 - && ((YailList)item).getObject(0) instanceof String) { - // this item is a valid object item - } else { - return false; - } + return JsonObject.canParseJSONObject(list); + } + + /** + * Check if the List can be parsed into a JSONObject + */ + @SimpleFunction( + description = "Check if the List can be parsed into a JsonObject") + public static boolean CanParseJsonObject(YailList list) { + return JsonObject.canParseJSONObject(list); + } + + /** + * Parse a json string / JSONArray / JSONObject / JsonArray / JsonObject into a List + */ + @SimpleFunction( + description = "Parse a json string / JsonArray / JsonObject into a List") + public static YailList Json2List(Object object) { + object = object instanceof String ? ParseJsonString((String) object) : object; + object = object instanceof JSONArray ? new JsonArray((JSONArray) object) : object; + object = object instanceof JSONObject ? new JsonObject((JSONObject) object) : object; + if (object instanceof JsonArray || object instanceof JsonObject) { + return ((JsonType) object).toList(); + } + throw new IllegalArgumentException(); + } + + + + /** + * Parse a list into a JsonArray / JsonObject + */ + @SimpleFunction( + description = "Parse a list into a JsonArray / JsonObject") + public static JsonType ParseList(YailList list) { + Object json = List2Json(list); + return json instanceof JSONArray + ? new JsonArray((JSONArray) json) + : new JsonObject((JSONObject) json); + } + + /** + * Parse a json string into a JsonArray / JsonObject + */ + @SimpleFunction( + description = "Parse a json string into a JsonArray / JsonObject") + public static JsonType ParseJsonString(String jsonString) { + if (jsonString == null || jsonString.isEmpty()) { + return new JsonArray(new JSONArray()); + } + try { + return new JsonArray(new JSONArray(jsonString)); + } catch (JSONException ok) {} + try { + return new JsonObject(new JSONObject(jsonString)); + } catch (JSONException ok) {} + return new JsonArray(new JSONArray()); + } + + + + /** + * Check if an object is null + */ + @SimpleFunction( + description = "Check if an object is null") + public static boolean isNull(Object object) { + return object == null; + } + + /** + * Check if an object is a JsonArray + */ + @SimpleFunction( + description = "Check if an object is a JsonArray") + public static boolean isAJsonArray(Object object) { + return object instanceof JsonArray; + } + + /** + * Check if an object is a JsonObject + */ + @SimpleFunction( + description = "Check if an object is a JsonObject") + public static boolean isAJsonObject(Object object) { + return object instanceof JsonObject; + } + + /** + * Check if an object is a JsonArray or a JsonObject + */ + @SimpleFunction( + description = "Check if an object is a JsonArray or a JsonObject") + public static boolean isAJsonArrayOrObject(Object object) { + return isAJsonArray(object) || isAJsonObject(object); + } + + + + /** + * Get the object value associated with an index. + * Return null if there is no object at that index. + */ + @SimpleFunction( + description = "Get the object value associated with an index. " + + "Return null if there is no object at that index.") + public static Object JsonArray_Get(JsonArray jsonArray, int index) { + return jsonArray.getArray().opt(index - 1); + } + + /** + * Put or replace an object value in the JSONArray. + * If the index is greater than the length of the JSONArray, + * then null elements will be added as necessary to pad it out. + */ + @SimpleFunction( + description = "Put or replace an object value in the JSONArray. " + + "If the index is greater than the length of the JSONArray, " + + "then null elements will be added as necessary to pad it out.") + public static void JsonArray_Put(JsonArray jsonArray, int index, Object value) throws JSONException { + if (index < 1) { + return; } - return true; + value = value instanceof JsonObject ? ((JsonObject) value).getObject() : value; + value = value instanceof JsonArray ? ((JsonArray) value).getArray() : value; + jsonArray.getArray().put(index - 1, + value instanceof YailList ? List2Json((YailList) value) : value); + // Throws: JSONException - (If the index is negative or) if the the value is an invalid number. + } + + /** + * Append an value. This increases the array's length by one. + */ + @SimpleFunction( + description = "Append an value. This increases the array's length by one.") + public static void JsonArray_Append(JsonArray jsonArray, Object value) { + value = value instanceof JsonObject ? ((JsonObject) value).getObject() : value; + value = value instanceof JsonArray ? ((JsonArray) value).getArray() : value; + jsonArray.getArray().put( + value instanceof YailList ? List2Json((YailList) value) : value); + } + + /** + * Remove an index and close the hole. + */ + @SimpleFunction( + description = "Remove an index and close the hole.") + public static void JsonArray_Remove(JsonArray jsonArray, int index) { + jsonArray.getArray().remove(index - 1); + } + + /** + * Get the number of elements in the JSONArray, included nulls. + */ + @SimpleFunction( + description = "Get the number of elements in the JsonArray, included nulls.") + public static int JsonArray_Length(JsonArray jsonArray) { + return jsonArray.getArray().length(); + } + + + + /** + * Determine if the JSONObject contains a specific key. + */ + @SimpleFunction( + description = "Determine if the JSONObject contains a specific key.") + public static boolean JsonObject_Contains(JsonObject jsonObject, String key) { + return jsonObject.getObject().has(key); + } + + /** + * Get the value object associated with a key. Returns null if there is no value. + */ + @SimpleFunction( + description = "Get the value object associated with a key. " + + "Returns null if there is no value.") + public static Object JsonObject_Get(JsonObject jsonObject, String key) { + return jsonObject.getObject().opt(key); } + + /** + * Put a key-value pair in the JsonObject. + */ + @SimpleFunction( + description = "Put a key-value pair in the JsonObject.") + public static void JsonObject_Put(JsonObject jsonObject, String key, Object value) throws JSONException { + value = value instanceof JsonObject ? ((JsonObject) value).getObject() : value; + value = value instanceof JsonArray ? ((JsonArray) value).getArray() : value; + jsonObject.getObject().putOpt(key, + value instanceof YailList ? List2Json((YailList) value) : value); + // Throws: JSONException - If the value is a non-finite number. + } + + /** + * Remove a key and its value, if present. + */ + @SimpleFunction( + description = "Remove a key and its value, if present.") + public static void JsonObject_Remove(JsonObject jsonObject, String key) { + jsonObject.getObject().remove(key); + } + + /** + * Get the number of keys stored in the JSONObject. + */ + @SimpleFunction( + description = "Get the number of keys stored in the JsonObject.") + public static int JsonObject_Size(JsonObject jsonObject) { + return jsonObject.getObject().length(); + } + } \ No newline at end of file diff --git a/README.md b/README.md index 9e76031..75072a9 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,136 @@ Some Json utilities for App Inventor (& Thunkable etc.) -Functions: +## Defined JsonType(s) + +Developers can use these as texts when using join-text block +For the other use, please be careful that it is not a strict String type and may causes problem. +So a prefered way to convert this into a strict String via a join-text block. + +* JsonArray + * JsonArray is a container of org.json.JSONArray. It is defined for functions like `JsonArray_Append` + * Can be parsed as a Json array String, e.g. `[]`, `["1", 2, true]` +* JsonObject + * JsonObject is a container of org.json.JSONObject. It is defined for functions like `JsonObject_Put` + * Can be parsed as a Json object String, e.g. `{}`, `{"number":1, "text":"foobar", "boolean":true}` + +## Functions + +![image](https://user-images.githubusercontent.com/22613139/44954326-fb764f00-aed2-11e8-9867-b1bee527ebf0.png) + +* CanParseJsonObject + * Check if the List can be parsed into a JsonObject + * parameters: + * list + * returns: + * boolean (true / false) +* Json2List + * Parse a json string / JsonArray / JsonObject into a List + * parameters: + * a Json String / JsonArray / JsonObject to be parsed + * returns: + * a list that represents the json + +![image](https://user-images.githubusercontent.com/22613139/44954323-edc0c980-aed2-11e8-85ac-ee28980c5cfd.png) + +* JsonArray_Append + * Append an value. This increases the array's length by one. + * parameters: + * JsonArray + * value (any) +* JsonArray_Get + * Get the object value associated with an index. Return null if there is no object at that index. + * parameters: + * JsonArray + * index of the item (index start from 1) + * returns: + * the value of the item +* JsonArray_Length + * Get the number of elements in the JsonArray, included nulls. + * parameters: + * JsonArray + * returns: + * the length of the JsonArray +* JsonArray_Put + * Put or replace an object value in the JSONArray. If the index is greater than the length of the JSONArray, then null elements will be added as necessary to pad it out. + * parameters: + * JsonArray + * index of the item (index start from 1) + * value to be put +* JsonArray_Remove + * Remove an index and close the hole. + * parameters: + * JsonArray + * index of the item (index start from 1) + +![image](https://user-images.githubusercontent.com/22613139/44954312-cc5fdd80-aed2-11e8-8d7b-fb933d5af9da.png) + +* JsonObject_Contains + * Determine if the JSONObject contains a specific key. + * parameters: + * JsonObject + * key (String) of the item + * returns: + * if the key exists in JsonObject +* JsonObject_Get + * Get the value object associated with a key. Returns null if there is no value. + * parameters: + * JsonObject + * key (String) of the item + * returns: + * value of the key paired up to +* JsonObject_Put + * Put a key-value pair in the JsonObject. + * parameters: + * JsonObject + * key (String) of the item + * value to be put +* JsonObject_Remove + * Remove a key and its value, if present. + * parameters: + * JsonObject + * key (String) of the item +* JsonObject_Size + * Get the number of keys stored in the JsonObject. + * parameters: + * JsonObject + * returns: + * number of the key-value pairs + +![image](https://user-images.githubusercontent.com/22613139/44954501-a425ae00-aed5-11e8-963b-6ae52286a613.png) * List2JsonString * Convert an App Inventor list into a json string * Any App Inventor list except for **lists of key-value pairs** would be considered as a JsonArray - * **list of key-value pairs:** list with all list items are lists with two items and the first list item is a text) (see sample below - * ![](https://user-images.githubusercontent.com/22613139/44947212-54df6f00-ae3c-11e8-86e6-50bc0d462a69.png) + * **list of key-value pairs:** list with all list items are lists with two items and the first list item is a text) (see sample below) + * parameters: + * list + * returns: + * corresponding Json String + * sample: + ![](https://user-images.githubusercontent.com/22613139/44947212-54df6f00-ae3c-11e8-86e6-50bc0d462a69.png) +* ParseJsonString + * Parse a json string into a JsonArray / JsonObject + * parameters: + * Json String + * returns: + * a corresponding JsonArray / JsonObject +* ParseList + * Parse a list into a JsonArray / JsonObject + * parameters: + * list + * returns: + * a corresponding JsonArray / JsonObject + +![image](https://user-images.githubusercontent.com/22613139/44954546-dc2cf100-aed5-11e8-9b53-a591579164bc.png) + +* isAJsonArray + * Check if an object is a JsonArray +* isAJsonArrayOrObject + * Check if an object is a JsonArray or a JsonObject +* isAJsonObject + * Check if an object is a JsonObject * isJsonObject - * Check if the List can be converted into a JsonObject \ No newline at end of file + * ***DEPRECATED*** via CanParseJsonObject +* isNull + * Check if an object is null \ No newline at end of file