From 8459368eca3e71d599b2d7bc2e263127cfa3b7d6 Mon Sep 17 00:00:00 2001 From: Aidan Follestad Date: Tue, 21 Feb 2017 22:47:44 -0600 Subject: [PATCH] 1.3.0 --- .idea/compiler.xml | 2 + .idea/modules.xml | 4 +- .idea/modules/ason.iml | 2 +- .../modules/{json_main.iml => ason_main.iml} | 3 +- .../modules/{json_test.iml => ason_test.iml} | 7 +- README.md | 102 +++++++++++++++-- build.gradle | 6 +- src/main/java/com/afollestad/ason/Ason.java | 71 ++++++------ .../java/com/afollestad/ason/AsonArray.java | 104 ++++++++++++++---- .../afollestad/ason/NullPathException.java | 11 ++ src/main/java/com/afollestad/ason/Util.java | 100 +++++++++++++---- .../com/afollestad/ason/AsonArrayTest.java | 30 ++++- .../com/afollestad/ason/AsonPathTest.java | 43 ++++++++ .../afollestad/ason/AsonSerializeTest.java | 18 ++- .../java/com/afollestad/ason/AsonTest.java | 79 ++++++++++++- 15 files changed, 476 insertions(+), 106 deletions(-) rename .idea/modules/{json_main.iml => ason_main.iml} (79%) rename .idea/modules/{json_test.iml => ason_test.iml} (75%) create mode 100644 src/main/java/com/afollestad/ason/NullPathException.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index eddb74b..dc635d8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,6 +2,8 @@ + + diff --git a/.idea/modules.xml b/.idea/modules.xml index a20905e..490ffd9 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,8 +3,8 @@ - - + + \ No newline at end of file diff --git a/.idea/modules/ason.iml b/.idea/modules/ason.iml index 1a6127b..d8b0921 100644 --- a/.idea/modules/ason.iml +++ b/.idea/modules/ason.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/json_main.iml b/.idea/modules/ason_main.iml similarity index 79% rename from .idea/modules/json_main.iml rename to .idea/modules/ason_main.iml index 7bc7d1c..5d853fa 100644 --- a/.idea/modules/json_main.iml +++ b/.idea/modules/ason_main.iml @@ -1,5 +1,5 @@ - + @@ -10,5 +10,6 @@ + \ No newline at end of file diff --git a/.idea/modules/json_test.iml b/.idea/modules/ason_test.iml similarity index 75% rename from .idea/modules/json_test.iml rename to .idea/modules/ason_test.iml index 951e47e..50ed7b8 100644 --- a/.idea/modules/json_test.iml +++ b/.idea/modules/ason_test.iml @@ -1,5 +1,5 @@ - + @@ -9,10 +9,11 @@ - + + - + \ No newline at end of file diff --git a/README.md b/README.md index a3332cd..d99412d 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ in the Android SDK. As we all know, those stock classes tend to be a pain. They 4. [Parsing and Building Arrays](https://github.com/afollestad/ason#parsing-and-building-arrays) 5. [Pretty Print](https://github.com/afollestad/ason#pretty-print) 6. [Paths](https://github.com/afollestad/ason#paths) + 1. [Dot Notation](https://github.com/afollestad/ason#dot-notation) + 2. [Index Notation](https://github.com/afollestad/ason#index-notation) + 3. [Escaping Periods and Dollar Signs](https://github.com/afollestad/ason#escaping-periods-and-dollar-signs) 7. [Serialization](https://github.com/afollestad/ason#serialization) 1. [Serializing Objects](https://github.com/afollestad/ason#serializing-objects) 2. [Serializing Arrays](https://github.com/afollestad/ason#serializing-arrays) @@ -49,7 +52,7 @@ The dependency is available via jCenter. ```Gradle dependencies { ... - compile 'com.afollestad:ason:1.2.0' + compile 'com.afollestad:ason:1.3.0' } ``` @@ -60,7 +63,7 @@ Since Android includes `org.json` classes, you'll want to exclude the copies pro ```Gradle dependencies { ... - compile('com.afollestad:ason:1.2.0') { + compile('com.afollestad:ason:1.3.0') { exclude group: 'org.json', module: 'json' } } @@ -238,10 +241,12 @@ String formatted = ason.toString(4); // 4 spaces being the indent size # Paths -Paths let you quickly add, retrieve, or remove items which are deeper down in your JSON hierarchy without manually -traversing. +Paths use periods and dollar signs to let you quickly add, retrieve, or remove items which are deeper +down in your JSON hierarchy without manually traversing. -Lets create an object using path keys: +### Dot Notation + +Lets create an object using a few dot notation keys: ```java Ason ason = new Ason() @@ -269,7 +274,9 @@ The above would construct this: As you can see, a child object is automatically created for you. We only use two levels, but you could create many more just by using more periods to separate child names. -We can use this same dot notation to retrieve these child values: +--- + +You can retrieve values from objects using dot notation: ```java Ason ason = // ... @@ -280,11 +287,21 @@ int day = ason.get("birthday.day"); int year = ason.get("birthday.year"); ``` -You can quickly check equality in objects... +You can do the same on arrays, but you need to specify the index of the object to pull from too: + +```java +AsonArray ason = // ... +String name = ason.get(1, "birthday.month"); +``` + +As a bonus, you can check equality without doing a manual value comparison: ```java Ason ason = // ... boolean birthYearCheck = ason.equal("birthday.year", 1995); + +AsonArray ason2 = // ... +boolean birthYearCheck2 = ason2.equal(2, "birthday.year", 1995); ``` And arrays: @@ -303,11 +320,47 @@ array.put(ason); boolean firstItemBirthYearCheck = array.equal(0, "birthday.year", 1995); ``` ---- +### Index Notation -If your keys actually have periods in them, you can escape periods: +To extend on dot notations in paths, you can use this notation to perform operations on array children. + +Take this JSON: + +```json +{ + "group_id": 1, + "title": "Hello, world!", + "participants": [ + { + "name": "Aidan", + "id": 2 + }, + { + "name": "Waverly", + "id": 1 + } + ] +} +``` + +You can retrieve the value of "name" in the second participant like this: ```java +Ason object = // ... +String name = object.get("participants.$1.name"); +``` + +The dollar sign followed by the number 1 indicates that you want the item at index 1 (position 2) +within the array called "participants". + +### Escaping Periods and Dollar Signs + +If your keys have literal periods in them, you can escape the periods so that they aren't used when +following a path. + +Take this JSON: + +```json { "files": { "test.txt": "Hello, world!" @@ -315,11 +368,42 @@ If your keys actually have periods in them, you can escape periods: } ``` +You can retrieve the value if the inner `test.txt` string like this: + ```java Ason ason = // ... String value = ason.get("files.test\\.txt"); ``` +We use two forward slashes since Java requires that you escape slashes. The literal string +is just *"files.test\.txt"*. + +--- + +You can also escape dollar signs which are normally used with index notation. + +Take this JSON: + +```json +{ + "participants": { + "$1": { + "name": "Waverly" + } + } +} +``` + +To retrieve the inner string called "name": + +```java +Ason ason = // ... +String value = ason.get("participants.\\$1.name"); +``` + +We use an escaped forward slash (\\) in front of the dollar sign to indicate that the dollar sign +is literal, and actually used in the name of a key. + --- # Serialization diff --git a/build.gradle b/build.gradle index 4c3171c..00e79b4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,8 @@ group 'com.afollestad' -version '1.2.0' +version '1.3.0' apply plugin: 'java' +apply plugin: 'idea' apply plugin: 'jacoco' apply plugin: 'com.novoda.bintray-release' @@ -23,13 +24,14 @@ repositories { dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' compile group: 'org.json', name: 'json', version: '20160810' + compile group: 'com.intellij', name: 'annotations', version: '12.0' } publish { userOrg = 'drummer-aidan' groupId = 'com.afollestad' artifactId = 'ason' - publishVersion = '1.2.0' + publishVersion = '1.3.0' website = 'https://github.com/afollestad/ason' } diff --git a/src/main/java/com/afollestad/ason/Ason.java b/src/main/java/com/afollestad/ason/Ason.java index f527f57..b6f9449 100644 --- a/src/main/java/com/afollestad/ason/Ason.java +++ b/src/main/java/com/afollestad/ason/Ason.java @@ -1,5 +1,7 @@ package com.afollestad.ason; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -20,9 +22,7 @@ private AsonSerializer serializer; private boolean loadedMyFields; - public Ason(JSONObject stock) { - if (stock == null) - stock = new JSONObject(); + public Ason(@NotNull JSONObject stock) { this.json = stock; this.serializer = AsonSerializer.get(); } @@ -31,16 +31,15 @@ public Ason() { this(new JSONObject()); } - public Ason(Map map) { + public Ason(@NotNull Map map) { this(); - if (map == null) return; for (String key : map.keySet()) { Object value = map.get(key); put(key, value); } } - public Ason(String json) { + public Ason(@NotNull String json) { try { this.json = new JSONObject(json); } catch (JSONException e) { @@ -83,9 +82,7 @@ private Ason putInternal(JSONArray intoArray, return this; } - public Ason put(String key, Object... values) { - if (key == null) - throw new IllegalArgumentException("Key cannot be null."); + public Ason put(@NotNull String key, Object... values) { Object insertObject; if (values == null || values.length == 1) { insertObject = values != null ? values[0] : null; @@ -106,18 +103,17 @@ public Ason put(String key, Object... values) { return this; } - public Ason remove(String key) { + public Ason remove(@NotNull String key) { json.remove(key); return this; } - public T get(String key) { + @Nullable public T get(@NotNull String key) { return get(key, (T) null); } - @SuppressWarnings("unchecked") public T get(String key, T defaultValue) { - if (key == null) - throw new IllegalArgumentException("Key cannot be null."); + @SuppressWarnings("unchecked") public T get( + @NotNull String key, @Nullable T defaultValue) { Object result; if (key.contains(".")) { final String[] splitKey = splitPath(key); @@ -139,75 +135,80 @@ public T get(String key) { } } - public boolean getBool(String key) { + public boolean getBool(@NotNull String key) { return getBool(key, false); } - public boolean getBool(String key, boolean defaultValue) { + public boolean getBool(@NotNull String key, boolean defaultValue) { return get(key, defaultValue); } - public String getString(String key) { + public String getString(@NotNull String key) { return getString(key, null); } - public String getString(String key, String defaultValue) { + @Nullable public String getString( + @NotNull String key, + @Nullable String defaultValue) { return get(key, defaultValue); } - public short getShort(String key) { + public short getShort(@NotNull String key) { return getShort(key, (short) 0); } - public short getShort(String key, short defaultValue) { + public short getShort(@NotNull String key, short defaultValue) { return get(key, defaultValue); } - public int getInt(String key) { + public int getInt(@NotNull String key) { return getInt(key, 0); } - public int getInt(String key, int defaultValue) { + public int getInt(@NotNull String key, int defaultValue) { return get(key, defaultValue); } - public long getLong(String key) { + public long getLong(@NotNull String key) { return getLong(key, 0L); } - public long getLong(String key, long defaultValue) { + public long getLong(@NotNull String key, long defaultValue) { return get(key, defaultValue); } - public float getFloat(String key) { + public float getFloat(@NotNull String key) { return getFloat(key, 0f); } - public float getFloat(String key, float defaultValue) { + public float getFloat(@NotNull String key, float defaultValue) { return get(key, defaultValue); } - public double getDouble(String key) { + public double getDouble(@NotNull String key) { return getDouble(key, 0d); } - public double getDouble(String key, double defaultValue) { + public double getDouble(@NotNull String key, double defaultValue) { return get(key, defaultValue); } - public Ason getJsonObject(String key) { + public Ason getJsonObject(@NotNull String key) { return get(key, (Ason) null); } - public AsonArray getJsonArray(String key) { + public AsonArray getJsonArray(@NotNull String key) { return get(key, (AsonArray) null); } - public T get(String key, Class cls) { + public T get(@NotNull String key, @NotNull Class cls) { return get(key, cls, null); } - @SuppressWarnings("Duplicates") public T get(String key, Class cls, T defaultValue) { + @SuppressWarnings("Duplicates") public T get( + @NotNull String key, + @NotNull Class cls, + @Nullable T defaultValue) { Object value = get(key, defaultValue); if (value == null) { return defaultValue; @@ -241,13 +242,11 @@ public T get(String key, Class cls) { } } - public boolean has(String key) { + public boolean has(@NotNull String key) { return get(key) != null; } - public boolean equal(String key, Object value) { - if (key == null) - throw new IllegalArgumentException("Key cannot be null."); + public boolean equal(@NotNull String key, @Nullable Object value) { Object actual = get(key); if (actual == null) { return value == null; diff --git a/src/main/java/com/afollestad/ason/AsonArray.java b/src/main/java/com/afollestad/ason/AsonArray.java index 606d0a3..86dbaf2 100644 --- a/src/main/java/com/afollestad/ason/AsonArray.java +++ b/src/main/java/com/afollestad/ason/AsonArray.java @@ -1,5 +1,7 @@ package com.afollestad.ason; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -13,7 +15,7 @@ /** * @author Aidan Follestad (afollestad) */ -@SuppressWarnings({"unchecked", "WeakerAccess", "unused"}) +@SuppressWarnings({"unchecked", "WeakerAccess", "unused", "SameParameterValue"}) public class AsonArray implements Iterable { private final JSONArray array; @@ -22,7 +24,7 @@ public AsonArray() { array = new JSONArray(); } - public AsonArray(String json) { + public AsonArray(@NotNull String json) { try { array = new JSONArray(json); } catch (JSONException e) { @@ -30,7 +32,7 @@ public AsonArray(String json) { } } - AsonArray(JSONArray internalArray) { + AsonArray(@NotNull JSONArray internalArray) { this.array = internalArray; } @@ -46,7 +48,7 @@ private void putInternal(T object) { } else if (object instanceof AsonArray) { insertObject = ((AsonArray) object).toStockJson(); } else if (object.getClass().isArray()) { - insertObject = AsonSerializer.get().serializeArray((Object[]) object); + insertObject = AsonSerializer.get().serializeArray(object); if (insertObject != null) insertObject = ((AsonArray) insertObject).toStockJson(); } else if (isList(object.getClass())) { insertObject = AsonSerializer.get().serializeList((List) object); @@ -59,13 +61,13 @@ private void putInternal(T object) { array.put(insertObject); } - public AsonArray add(T... objects) { + public AsonArray add(@NotNull T... objects) { for (T obj : objects) putInternal(obj); return this; } - public T get(int index) { + @Nullable public T get(int index) { try { Object value = array.opt(index); if (value instanceof JSONObject) { @@ -79,19 +81,70 @@ public T get(int index) { } } - public Ason getJsonObject(int index) { - return new Ason(array.optJSONObject(index)); + @Nullable public Ason getJsonObject(int index) { + if (index < 0 || index > array.length() - 1) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds for this array!"); + } + try { + JSONObject object = array.optJSONObject(index); + if (object == null) { + return null; + } + return new Ason(object); + } catch (JSONException e) { + throw new IllegalStateException("Could not get a JSON object from this array!", e); + } } - public AsonArray getJsonArray(int index) { - return new AsonArray(array.getJSONArray(index)); + @Nullable public AsonArray getJsonArray(int index) { + if (index < 0 || index > array.length() - 1) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds for this array!"); + } + try { + JSONArray ary = array.optJSONArray(index); + if (ary == null) { + return null; + } + return new AsonArray(ary); + } catch (JSONException e) { + throw new IllegalStateException("Could not get a JSON array from this array!", e); + } } public T get(int index, Class cls) { + return get(index, null, cls); + } + + public T get(int index, @Nullable String path) { + return get(index, path, null); + } + + public T get(int index, @Nullable String path, Class cls) { + if (index < 0 || index > array.length() - 1) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds for this array!"); + } Object value = array.opt(index); + if (path != null) { + if (!(value instanceof JSONObject) + && !(value instanceof Ason)) { + throw new IllegalArgumentException("Cannot get from an AsonArray using a " + + "path when array items are not JSON objects."); + } + if (value instanceof JSONObject) { + value = getPathValue((JSONObject) value, path, splitPath(path)); + } else { + value = getPathValue(((Ason) value).toStockJson(), path, splitPath(path)); + } + cls = (Class) value.getClass(); + } + if (value == null) { return null; - } else if (isPrimitive(cls) || + } + if (cls == null) { + throw new IllegalArgumentException("cls parameter cannot be null!"); + } + if (isPrimitive(cls) || cls == JSONObject.class || cls == JSONArray.class || cls == Ason.class || @@ -99,19 +152,22 @@ public T get(int index, Class cls) { return (T) value; } else if (cls.isArray()) { if (!(value instanceof JSONArray)) { - throw new IllegalStateException("Expected a JSONArray to convert to " + cls.getName() + ", didn't find one."); + throw new IllegalStateException("Expected a JSONArray to convert to " + + cls.getName() + ", didn't find one."); } AsonArray array = new AsonArray<>((JSONArray) value); return (T) AsonSerializer.get().deserializeArray(array, cls.getComponentType()); } else if (isList(cls)) { if (!(value instanceof JSONArray)) { - throw new IllegalStateException("Expected a JSONArray to convert to " + cls.getName() + ", didn't find one."); + throw new IllegalStateException("Expected a JSONArray to convert to " + + cls.getName() + ", didn't find one."); } AsonArray array = new AsonArray<>((JSONArray) value); return (T) AsonSerializer.get().deserializeList(array, cls.getComponentType()); } else { if (!(value instanceof JSONObject)) { - throw new IllegalStateException("Expected a JSONObject to convert to " + cls.getName() + ", didn't find one."); + throw new IllegalStateException("Expected a JSONObject to convert to " + + cls.getName() + ", didn't find one."); } Ason object = new Ason((JSONObject) value); return AsonSerializer.get().deserialize(object, cls); @@ -119,11 +175,14 @@ public T get(int index, Class cls) { } public AsonArray remove(int index) { + if (index < 0 || index > array.length() - 1) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds for this array!"); + } array.remove(index); return this; } - public boolean equal(int index, Object value) { + public boolean equal(int index, @Nullable T value) { T actual = get(index); if (actual == null) { return value == null; @@ -131,13 +190,18 @@ public boolean equal(int index, Object value) { return actual.equals(value); } - public boolean equal(int index, String path, Object value) { - if (index >= size()) { - throw new IndexOutOfBoundsException("Index " + index + " is out of bounds for this array of size " + size() + "."); + public boolean equal(int index, String path, @Nullable Object value) { + if (index < 0 || index > array.length() - 1) { + throw new IndexOutOfBoundsException("Index " + index + " is out of bounds for this array!"); } T arrayEntry = get(index); + if (arrayEntry == null) { + return value == null; + } if (!(arrayEntry instanceof JSONObject || arrayEntry instanceof Ason)) { - throw new InvalidPathException("You cannot use equal(int, String, Object) in AsonArray when the array contains primitives (" + arrayEntry.getClass().getName() + ")."); + throw new InvalidPathException("You cannot use equal(int, String, " + + "Object) in AsonArray when the array contains primitives (" + + arrayEntry.getClass().getName() + ")."); } JSONObject encloser; if (arrayEntry instanceof JSONObject) { @@ -160,7 +224,7 @@ public boolean isEmpty() { return size() == 0; } - private List toList() { + @NotNull private List toList() { List list = new ArrayList<>(array.length()); for (int i = 0; i < array.length(); i++) { list.add((T) array.opt(i)); diff --git a/src/main/java/com/afollestad/ason/NullPathException.java b/src/main/java/com/afollestad/ason/NullPathException.java new file mode 100644 index 0000000..2510b3f --- /dev/null +++ b/src/main/java/com/afollestad/ason/NullPathException.java @@ -0,0 +1,11 @@ +package com.afollestad.ason; + +/** + * @author Aidan Follestad (afollestad) + */ +public class NullPathException extends InvalidPathException { + + NullPathException(String message) { + super(message); + } +} diff --git a/src/main/java/com/afollestad/ason/Util.java b/src/main/java/com/afollestad/ason/Util.java index ddff6c9..f5bb89f 100644 --- a/src/main/java/com/afollestad/ason/Util.java +++ b/src/main/java/com/afollestad/ason/Util.java @@ -1,5 +1,6 @@ package com.afollestad.ason; +import org.json.JSONArray; import org.json.JSONObject; import java.lang.reflect.Constructor; @@ -34,50 +35,99 @@ static String[] splitPath(String key) { return result.toArray(new String[result.size()]); } - static JSONObject followPath(JSONObject encloser, + private static boolean isNumber(String string) { + for (char c : string.toCharArray()) { + if (!Character.isDigit(c)) { + return false; + } + } + return true; + } + + static JSONObject followPath(JSONObject wrapper, String key, String[] splitKey, boolean createMissing) { - Object parent = encloser.opt(splitKey[0]); - if (parent != null && !(parent instanceof JSONObject)) { + // Get value for the first path key + Object parent = wrapper.opt(splitKey[0]); + if (parent != null + && !(parent instanceof JSONObject) + && !(parent instanceof JSONArray)) { throw new InvalidPathException("First component of key " + key + " refers to " + - splitKey[0] + ", which is not an object (it's a " + parent.getClass().getName() + ")."); + splitKey[0] + ", which is not an object or array (it's a " + + parent.getClass().getName() + ")."); } else if (parent == null) { if (createMissing) { parent = new JSONObject(); - encloser.put(splitKey[0], parent); + wrapper.put(splitKey[0], parent); } else { - throw new InvalidPathException("No object found for the first component of key " + + throw new InvalidPathException("No object or array found for the first component of key " + key + " (" + splitKey[0] + ")."); } } + + // Loop through following entries for (int i = 1; i < splitKey.length - 1; i++) { - Object current = ((JSONObject) parent).opt(splitKey[i]); - if (current != null && !(current instanceof JSONObject)) { + String currentKey = splitKey[i]; + if (currentKey.startsWith("\\$")) { + // A dollar sign is escaped + currentKey = currentKey.substring(1); + } else if (currentKey.startsWith("$")) { + if (isNumber(currentKey.substring(1))) { + // This is an array index key + final int index = Integer.parseInt(currentKey.substring(1)); + Object current = ((JSONArray) parent).opt(index); + if (current != null + && !(current instanceof JSONObject) + && !(current instanceof JSONArray)) { + throw new InvalidPathException("Item at index" + i + " of current entry " + + " refers to " + currentKey + ", which is not an object or array (it's a " + + current.getClass().getName() + ")."); + } else if (current == null) { + if (createMissing) { + current = new JSONObject(); + ((JSONObject) parent).put(currentKey, current); + } else { + throw new NullPathException("Item at index " + i + " " + + "of current entry refers to a null or out of bounds entry."); + } + } + parent = current; + continue; + } + } + + // Key is an object name + Object current = ((JSONObject) parent).opt(currentKey); + if (current != null + && !(current instanceof JSONObject) + && !(current instanceof JSONArray)) { throw new InvalidPathException("Component " + (i + 1) + " of key " + key + - " refers to " + splitKey[i] + ", which is not an object (most likely a primitive)."); + " refers to " + currentKey + ", which is not an object or array (it's a " + + current.getClass().getName() + ")."); } else if (current == null) { if (createMissing) { current = new JSONObject(); - ((JSONObject) parent).put(splitKey[i], current); + ((JSONObject) parent).put(currentKey, current); } else { - throw new InvalidPathException("Component " + (i + 1) + " of key " + key + - " refers to " + splitKey[i] + ", which is not an object (most likely a primitive)."); + throw new NullPathException("Item at index " + i + " " + + "of current entry refers to a null or out of bounds entry."); } } parent = current; } + return (JSONObject) parent; } @SuppressWarnings("unchecked") static T getPathValue( - JSONObject encloser, + JSONObject wrapper, String key, String[] splitKey) { if (splitKey.length == 1) { - return (T) encloser.get(key); + return (T) wrapper.get(key); } - JSONObject target = followPath(encloser, key, splitKey, false); + JSONObject target = followPath(wrapper, key, splitKey, false); return (T) target.opt(splitKey[splitKey.length - 1]); } @@ -98,22 +148,30 @@ private static Constructor getDefaultConstructor( Map, Constructor> cache) { if (cache != null) { Constructor ctor = cache.get(cls); - if (ctor != null) return ctor; + if (ctor != null) { + return ctor; + } } final Constructor[] constructorArray = cls.getDeclaredConstructors(); Constructor constructor = null; for (Constructor ct : constructorArray) { - if (ct.getParameterTypes() != null && ct.getParameterTypes().length != 0) + if (ct.getParameterTypes() != null + && ct.getParameterTypes().length != 0) { continue; + } constructor = ct; - if (constructor.getGenericParameterTypes().length == 0) + if (constructor.getGenericParameterTypes().length == 0) { break; + } + } + if (constructor == null) { + throw new IllegalStateException( + "No default constructor found for " + cls.getName()); } - if (constructor == null) - throw new IllegalStateException("No default constructor found for " + cls.getName()); constructor.setAccessible(true); - if (cache != null) + if (cache != null) { cache.put(cls, constructor); + } return constructor; } diff --git a/src/test/java/com/afollestad/ason/AsonArrayTest.java b/src/test/java/com/afollestad/ason/AsonArrayTest.java index f6365f8..61e5cfc 100644 --- a/src/test/java/com/afollestad/ason/AsonArrayTest.java +++ b/src/test/java/com/afollestad/ason/AsonArrayTest.java @@ -23,7 +23,8 @@ public class AsonArrayTest { } @Test public void builder_test() { - String expected = "[{\"name\":\"Aidan\",\"_id\":1,\"attrs\":{\"priority\":2}},{\"name\":\"Waverly\",\"_id\":2,\"attrs\":{\"priority\":1}}]"; + String expected = "[{\"name\":\"Aidan\",\"_id\":1,\"attrs\":{\"priority\":2}}," + + "{\"name\":\"Waverly\",\"_id\":2,\"attrs\":{\"priority\":1}}]"; assertEquals(array.toString(), expected); } @@ -38,4 +39,31 @@ public class AsonArrayTest { assertTrue(array.equal(1, "_id", 2)); assertTrue(array.equal(1, "attrs.priority", 1)); } + + @Test public void remove_test() { + Ason one = new Ason() + .put("_id", 1) + .put("name", "Aidan") + .put("attrs.priority", 2); + Ason two = new Ason() + .put("_id", 2) + .put("name", "Waverly") + .put("attrs.priority", 1); + array = new AsonArray() + .add(one) + .add(two); + array.remove(0); + assertEquals(two, array.get(0)); + assertTrue(array.equal(0, two)); + } + + @Test public void test_pretty_print() { + array = new AsonArray() + .add(new Ason().put("_id", 1)) + .add(new Ason().put("_id", 2)); + assertEquals("[\n" + + " {\"_id\": 1},\n" + + " {\"_id\": 2}\n" + + "]", array.toString(4)); + } } diff --git a/src/test/java/com/afollestad/ason/AsonPathTest.java b/src/test/java/com/afollestad/ason/AsonPathTest.java index c4cc1fb..f5c06c5 100644 --- a/src/test/java/com/afollestad/ason/AsonPathTest.java +++ b/src/test/java/com/afollestad/ason/AsonPathTest.java @@ -12,6 +12,11 @@ public class AsonPathTest { .put("person._id", 3) .put("person.name", "Aidan") .put("person.age", 21); + + assertEquals(3, ason.get("person._id")); + assertEquals("Aidan", ason.get("person.name")); + assertEquals(21, ason.get("person.age")); + String output = "{\"person\":{\"name\":\"Aidan\",\"_id\":3,\"age\":21}}"; assertEquals(output, ason.toString()); } @@ -19,6 +24,7 @@ public class AsonPathTest { @Test public void from_string_test() { String input = "{\"person\":{\"name\":\"Aidan\",\"_id\":3,\"age\":21}}"; Ason ason = new Ason(input); + assertEquals(ason.size(), 1); assertTrue(ason.equal("person.name", "Aidan")); assertTrue(ason.equal("person._id", 3)); @@ -31,8 +37,45 @@ public class AsonPathTest { @AsonName(name = "person.name") String name = "Aidan"; @AsonName(name = "person.age") int age = 21; }; + assertEquals(ason.size(), 1); + assertEquals(3, ason.get("person._id")); + assertEquals("Aidan", ason.get("person.name")); + assertEquals(21, ason.get("person.age")); + String output = "{\"person\":{\"name\":\"Aidan\",\"_id\":3,\"age\":21}}"; assertEquals(output, ason.toString()); } + + @Test public void array_get_path_test() { + String input = "[{\"body\":\"Hello, world\",\"sender\":{\"name\":\"Aidan\",\"id\":2}}," + + "{\"body\":\"Hello, world\",\"sender\":{\"name\":\"Waverly\",\"id\":1}}," + + "{\"body\":\"Hello, world\",\"sender\":{\"name\":\"Jeff\",\"id\":3}}]"; + AsonArray array = new AsonArray(input); + + assertEquals("Waverly", array.get(1, "sender.name")); + assertEquals(3, array.get(2, "sender.id")); + } + + @Test public void test_escape_period() { + String input = "{\"files\":{\"test.txt\":\"Hello, world!\"}}"; + Ason object = new Ason(input); + assertEquals("Hello, world!", object.get("files.test\\.txt")); + } + + @Test public void test_index_notation() { + String input = "{\"group_id\":1,\"title\":\"Hello, world!\"," + + "\"participants\":[{\"name\":\"Aidan\",\"id\":2}," + + "{\"name\":\"Waverly\",\"id\":1}]}"; + Ason object = new Ason(input); + + assertEquals("Waverly", object.get("participants.$1.name")); + assertEquals(2, object.get("participants.$0.id")); + } + + @Test public void test_escape_dollarsign() { + String input = "{\"participants\":{\"$1\":{\"name\":\"Waverly\"}}}"; + Ason object = new Ason(input); + assertEquals("Waverly", object.get("participants.\\$1.name")); + } } diff --git a/src/test/java/com/afollestad/ason/AsonSerializeTest.java b/src/test/java/com/afollestad/ason/AsonSerializeTest.java index 28d836d..ebbe36f 100644 --- a/src/test/java/com/afollestad/ason/AsonSerializeTest.java +++ b/src/test/java/com/afollestad/ason/AsonSerializeTest.java @@ -109,7 +109,8 @@ public Person() { // @Test public void test_deserialize() { - String input = "{\"name\":\"Aidan\",\"_id\":2,\"age\":21,\"spouse\":{\"name\":\"Waverly\",\"_id\":6,\"age\":19}}"; + String input = "{\"name\":\"Aidan\",\"_id\":2,\"age\":21," + + "\"spouse\":{\"name\":\"Waverly\",\"_id\":6,\"age\":19}}"; Ason ason = new Ason(input); Person person = ason.deserialize(Person.class); assertEquals(person.name, "Aidan"); @@ -122,7 +123,8 @@ public Person() { } @Test public void test_deserialize_array() { - String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21},{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; + String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21}," + + "{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; AsonArray array = new AsonArray<>(input); Person[] people = array.deserialize(Person[].class); @@ -136,7 +138,8 @@ public Person() { } @Test public void test_deserialize_list() { - String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21},{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; + String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21}," + + "{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; AsonArray array = new AsonArray<>(input); List people = array.deserializeList(Person.class); @@ -150,19 +153,22 @@ public Person() { } @Test public void test_deserialize_string_object() { - String input = "{\"name\":\"Aidan\",\"_id\":2,\"age\":21,\"spouse\":{\"name\":\"Waverly\",\"_id\":6,\"age\":19}}"; + String input = "{\"name\":\"Aidan\",\"_id\":2,\"age\":21," + + "\"spouse\":{\"name\":\"Waverly\",\"_id\":6,\"age\":19}}"; Person object = Ason.deserialize(input, Person.class); assertNotNull(object); } @Test public void test_deserialize_string_array() { - String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21},{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; + String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21}," + + "{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; Person[] object = Ason.deserialize(input, Person[].class); assertEquals(object.length, 2); } @Test public void test_deserialize_string_list() { - String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21},{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; + String input = "[{\"name\":\"Aidan\",\"_id\":1,\"age\":21}," + + "{\"name\":\"Waverly\",\"_id\":2,\"age\":19}]"; List object = Ason.deserializeList(input, Person.class); assertEquals(object.size(), 2); } diff --git a/src/test/java/com/afollestad/ason/AsonTest.java b/src/test/java/com/afollestad/ason/AsonTest.java index 26c098b..986f855 100644 --- a/src/test/java/com/afollestad/ason/AsonTest.java +++ b/src/test/java/com/afollestad/ason/AsonTest.java @@ -2,11 +2,21 @@ import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; public class AsonTest { + @Test public void invalid_json_test() { + try { + new Ason("Hello, world!"); + assertFalse("No exception thrown for invalid JSON!", false); + } catch (InvalidJsonException ignored) { + } + } + @Test public void builder_test() { Ason ason = new Ason() .put("_id", 3) @@ -16,6 +26,55 @@ public class AsonTest { assertEquals(output, ason.toString()); } + @Test public void from_map_test() { + Map map = new HashMap<>(2); + map.put("name", "Aidan"); + map.put("born", 1995); + Ason ason = new Ason(map); + assertEquals("Aidan", ason.get("name")); + assertEquals(1995, ason.get("born")); + } + + @Test public void test_has() { + Ason ason = new Ason() + .put("_id", 3) + .put("name", "Aidan") + .put("age", 21); + assertTrue(ason.has("name")); + assertFalse(ason.has("idk")); + } + + @Test public void test_is_null() { + Ason ason = new Ason() + .put("_id", 3) + .put("name", null) + .put("age", 21); + assertTrue(ason.isNull("name")); + assertFalse(ason.isNull("age")); + } + + @Test public void test_remove_key() { + Ason ason = new Ason() + .put("_id", 3) + .put("name", "Aidan") + .put("age", 21); + ason.remove("name"); + assertEquals("{\"_id\":3,\"age\":21}", ason.toString()); + } + + @Test public void test_equals() { + Ason one = new Ason().put("_id", 3); + Ason two = new Ason().put("_id", 3); + Ason three = new Ason().put("_id", 4); + assertEquals(one, two); + assertNotEquals(one, three); + } + + @Test public void test_hashcode() { + Ason ason = new Ason().put("_id", 3); + assertEquals(ason.hashCode(), ason.toStockJson().hashCode()); + } + @Test public void from_string_test() { String input = "{\"name\":\"Aidan\",\"_id\":3,\"age\":21}"; Ason ason = new Ason(input); @@ -23,7 +82,8 @@ public class AsonTest { assertTrue(ason.equal("name", "Aidan")); assertTrue(ason.equal("_id", 3)); assertTrue(ason.equal("age", 21)); - assertEquals(ason.get("non-existent", 69).intValue(), 69); + assertEquals(ason.get("non-existent", + 69).intValue(), 69); } @Test public void anon_fields_test() { @@ -42,6 +102,17 @@ public class AsonTest { Ason ason = new Ason() .put("greetings", one, two); assertEquals(ason.size(), 1); - assertEquals(ason.toString(), "{\"greetings\":[{\"greeting\":\"Hey\"},{\"greeting\":\"hello\"}]}"); + assertEquals(ason.toString(), "{\"greetings\":[{\"greeting\":" + + "\"Hey\"},{\"greeting\":\"hello\"}]}"); + } + + @Test public void test_pretty_print() { + Ason ason = new Ason() + .put("name", "Aidan") + .put("born", 1995); + assertEquals("{" + + "\n \"born\": 1995," + + "\n \"name\": \"Aidan\"" + + "\n}", ason.toString(4)); } }