diff --git a/README.md b/README.md index 9b0ec48a..f0555448 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,15 @@ # Changelog -### *V SNAPSHOOT* - - * empty +### *V 2.3* +* Patch 37 [issue 37](http://code.google.com/p/json-smart/issues/detail?id=37) +* explicite support of char 127 [issue 18](http://code.google.com/p/json-smart/issues/detail?id=18) +* integrate json-smart-action from Eitan Raviv [PR 31](https://github.com/netplex/json-smart-v2/pull/31) +* Remove hard codded e.printStackTrace() [issue 33](https://github.com/netplex/json-smart-v2/issues/33) + +### *V 2.2.2* + * Fix support for default java datetime format for US locale + * update my time Zone from Paris to San Francisco. ### *V 2.2.1* * Fix issue in strict mode [issue gh-17](https://github.com/netplex/json-smart-v2/issues/17) diff --git a/accessors-smart/pom.xml b/accessors-smart/pom.xml index 4571fb61..9d23e49b 100644 --- a/accessors-smart/pom.xml +++ b/accessors-smart/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.minidev accessors-smart - 1.2 + 1.2-SNAPSHOT ASM based accessors helper used by json-smart Java reflect give poor performance on getter setter an constructor calls, accessors-smart use ASM to speed up those calls. @@ -18,7 +18,7 @@ uriel Uriel Chemouni uchemouni@gmail.com - GMT+1 + GMT-7 @@ -257,8 +257,6 @@ net.minidev.asm, net.minidev.asm.ex - - asm;groupId=org.ow2.asm;inline=true diff --git a/json-smart-action/.gitignore b/json-smart-action/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/json-smart-action/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/json-smart-action/pom.xml b/json-smart-action/pom.xml new file mode 100644 index 00000000..e9ea3a3a --- /dev/null +++ b/json-smart-action/pom.xml @@ -0,0 +1,259 @@ + + + + net.minidev + minidev-parent + 2.3-SNAPSHOT + + 4.0.0 + json-smart-action + JSON Small and Fast Parser + + JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language. + + bundle + http://www.minidev.net/ + + Chemouni Uriel + http://www.minidev.net/ + + + + uriel + Uriel Chemouni + uchemouni@gmail.com + GMT-7 + + + + + erav + Eitan Raviv + adoneitan@gmail.com + GMT+2 + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + All files under Apache 2 + + + + UTF-8 + 1.5 + 1.5 + + + + junit + junit + test + + + net.minidev + json-smart + + + + + scm:git:https://github.com/netplex/json-smart-v2.git + scm:git:https://github.com/netplex/json-smart-v2.git + https://github.com/netplex/json-smart-v2 + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + release-sign-artifacts + + + + performRelease + true + + + + + 2C8DF6EC + + + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.2 + + forked-path + -Psonatype-oss-release + false + false + release + deploy + + + + + + + include-sources + + + + / + true + src/main/java + + **/*.java + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + bind-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + UTF-8 + 1.6 + 1.6 + + **/.svn/* + **/.svn + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.7 + + UTF-8 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + **/.svn/* + **/.svn + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + false + + + + + attach-javadocs + + jar + + + + + + org.apache.felix + maven-bundle-plugin + 3.0.0 + true + + + ${project.groupId}.${project.artifactId} + ${project.artifactId} + ${project.version} + + net.minidev.json, net.minidev.json.annotate, net.minidev.json.parser, net.minidev.json.reader, net.minidev.json.writer + + + + + + + \ No newline at end of file diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/ElementRemover.java b/json-smart-action/src/main/java/net/minidev/json/actions/ElementRemover.java new file mode 100644 index 00000000..d34a8346 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/ElementRemover.java @@ -0,0 +1,46 @@ +package net.minidev.json.actions; + +import net.minidev.json.JSONObject; +import net.minidev.json.actions.traverse.JSONTraverser; +import net.minidev.json.actions.traverse.RemoveElementsJsonAction; +import net.minidev.json.actions.traverse.JSONTraverseAction; + +import java.util.*; + +/** + * Removes key:value elements from every node of a {@link JSONObject} matching the list of user-specified elements. + *

+ * An element to remove must be specified as a key:value pair + *

+ * Usage Example: + *

+ * To remove the element k2:v2 from the {@link JSONObject} {k0:{k2:v2, k3:v3}, k1:{k2:v2, k4:v4}} use the remover like so: + *

+ * PathRemover pr = new PathRemover("k2.v2");
+ * JSONObject cleanObject = pr.remove(new JSONObject(...));
+ * 
+ * The resulting object 'cleanObject' would be {k0:{k3:v3}, k1:{k4:v4}} + *

+ * See unit tests for more examples + * + * @author adoneitan@gmail.com + * + */ +public class ElementRemover { + private Map elementsToRemove; + + public ElementRemover(Map elementsToRemove) { + this.elementsToRemove = elementsToRemove == null ? Collections. emptyMap() : elementsToRemove; + } + + public ElementRemover(JSONObject elementsToRemove) { + this.elementsToRemove = elementsToRemove == null ? Collections. emptyMap() : elementsToRemove; + } + + public JSONObject remove(JSONObject objectToClean) { + JSONTraverseAction strategy = new RemoveElementsJsonAction(this.elementsToRemove); + JSONTraverser traversal = new JSONTraverser(strategy); + traversal.traverse(objectToClean); + return (JSONObject) strategy.result(); + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/PathLocator.java b/json-smart-action/src/main/java/net/minidev/json/actions/PathLocator.java new file mode 100644 index 00000000..f62efb51 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/PathLocator.java @@ -0,0 +1,65 @@ +package net.minidev.json.actions; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.DotDelimiter; +import net.minidev.json.actions.path.PathDelimiter; +import net.minidev.json.actions.traverse.JSONTraverser; +import net.minidev.json.actions.traverse.LocatePathsJsonAction; +import net.minidev.json.actions.traverse.JSONTraverseAction; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Searches for paths in a {@link JSONObject} and returns those found. + *

+ * Traverses the specified {@link JSONObject} searching for nodes whose paths (from the root down) match + * any of the user-specified paths. The paths that match are returned. + *

+ * A path to locate must be specified in the n-gram format - a list of keys from the root down separated by dots: + * K0[[[[.K1].K2].K3]...] + *
+ * A key to the right of a dot is a direct child of a key to the left of a dot. Keys with a dot in their name are + * not supported. + *

+ * + * @author adoneitan@gmail.com + */ +public class PathLocator { + protected List pathsToFind; + protected PathDelimiter pathDelimiter = new DotDelimiter().withAcceptDelimiterInNodeName(false); + + public PathLocator(JSONArray pathsToFind) { + if (pathsToFind == null || pathsToFind.isEmpty()) { + this.pathsToFind = Collections.emptyList(); + } else { + this.pathsToFind = new ArrayList(); + for (Object s : pathsToFind) { + this.pathsToFind.add((String) s); + } + } + } + + public PathLocator(List pathsToFind) { + this.pathsToFind = pathsToFind == null || pathsToFind.size() == 0 ? Collections. emptyList() : pathsToFind; + } + + public PathLocator(String... pathsToFind) { + this.pathsToFind = pathsToFind == null || pathsToFind.length == 0 ? Collections. emptyList() : Arrays.asList(pathsToFind); + } + + public PathLocator with(PathDelimiter pathDelimiter) { + this.pathDelimiter = pathDelimiter; + return this; + } + + public List locate(JSONObject object) { + JSONTraverseAction action = new LocatePathsJsonAction(this.pathsToFind, pathDelimiter); + JSONTraverser traversal = new JSONTraverser(action).with(pathDelimiter); + traversal.traverse(object); + return (List) action.result(); + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/PathRemover.java b/json-smart-action/src/main/java/net/minidev/json/actions/PathRemover.java new file mode 100644 index 00000000..af486e3a --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/PathRemover.java @@ -0,0 +1,65 @@ +package net.minidev.json.actions; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.traverse.JSONTraverser; +import net.minidev.json.actions.traverse.RemovePathsJsonAction; +import net.minidev.json.actions.traverse.JSONTraverseAction; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Removes branches of nodes from a {@link JSONObject} matching the list of user-specified paths. + *

+ * A path to remove must be specified in the n-gram format - a list of keys from the root down separated by dots: + * K0[[[[.K1].K2].K3]...] + *
+ * A key to the right of a dot is a direct child of a key to the left of a dot. Keys with a dot in their name are + * not supported. + *

+ * Usage Example: + *

+ * To remove the field k1.k2 from the {@link JSONObject} {k1:{k2:v2}, k3:{k4:v4}} use the remover like so: + *

+ * PathRemover pr = new PathRemover("k1.k2");
+ * JSONObject cleanObject = pr.remove(new JSONObject(...));
+ * 
+ * The resulting object 'cleanObject' would be {k1:{k3:{k4:v4}}} + *

+ * See unit tests for more examples + * + * @author adoneitan@gmail.com + * + */ +public class PathRemover { + protected List pathsToRemove; + + public PathRemover(JSONArray pathsToRemove) { + if (pathsToRemove == null || pathsToRemove.isEmpty()) { + this.pathsToRemove = Collections.emptyList(); + } else { + this.pathsToRemove = new ArrayList(); + for (Object s : pathsToRemove) { + this.pathsToRemove.add((String) s); + } + } + } + + public PathRemover(List pathsToRemove) { + this.pathsToRemove = pathsToRemove == null || pathsToRemove.size() == 0 ? Collections. emptyList() : pathsToRemove; + } + + public PathRemover(String... pathsToRemove) { + this.pathsToRemove = pathsToRemove == null || pathsToRemove.length == 0 ? Collections. emptyList() : Arrays.asList(pathsToRemove); + } + + public JSONObject remove(JSONObject objectToClean) { + JSONTraverseAction strategy = new RemovePathsJsonAction(this.pathsToRemove); + JSONTraverser traversal = new JSONTraverser(strategy); + traversal.traverse(objectToClean); + return (JSONObject) strategy.result(); + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/PathReplicator.java b/json-smart-action/src/main/java/net/minidev/json/actions/PathReplicator.java new file mode 100644 index 00000000..3841c946 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/PathReplicator.java @@ -0,0 +1,68 @@ +package net.minidev.json.actions; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.navigate.CopyPathsAction; +import net.minidev.json.actions.navigate.JSONNavigator; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Creates a copy of a {@link JSONObject} consisting only of the nodes on the user-specified paths. + *

+ * Paths that do not exist in the specified object are ignored silently. Specifying an empty list of paths + * to copy or only non-existent paths will result in an empty object being returned. + *

+ * A path to copy must be specified in the n-gram format - a list of keys from the root down separated by dots: + * K0[[[[.K1].K2].K3]...] + *
+ * A key to the right of a dot is a direct child of a key to the left of a dot. Keys with a dot in their name are + * not supported. + *

+ * Sample usage: + *

+ * To replicate the branch k1.k2 from {k1:{k2:v2}, k3:{k4:v4}} use the {@link PathReplicator} like so: + *

+ * PathReplicator pr = new {@link PathReplicator}("k1.k2")
+ * JSONObject copiedObject = pr.copy(new JSONObject(...))
+ * 
+ * The resulting object 'copiedObject' would be {k1:{k2:v2}} + *

+ * see unit tests for more examples + * + * @author adoneitan@gmail.com + * @since 15 March 2016. + */ +public class PathReplicator { + protected List pathsToCopy; + + public PathReplicator(JSONArray pathsToCopy) { + if (pathsToCopy == null || pathsToCopy.isEmpty()) { + this.pathsToCopy = Collections.emptyList(); + } else { + this.pathsToCopy = new LinkedList(); + for (Object s : pathsToCopy) { + this.pathsToCopy.add((String) s); + } + } + } + + public PathReplicator(List pathsToCopy) { + this.pathsToCopy = pathsToCopy == null || pathsToCopy.size() == 0 ? Collections. emptyList() : pathsToCopy; + } + + public PathReplicator(String... pathsToCopy) { + this.pathsToCopy = pathsToCopy == null || pathsToCopy.length == 0 ? Collections. emptyList() + : new LinkedList(Arrays.asList(pathsToCopy)); + } + + public JSONObject replicate(JSONObject sourceObj) throws Exception { + CopyPathsAction s = new CopyPathsAction(); + JSONNavigator n = new JSONNavigator(s, pathsToCopy); + n.nav(sourceObj); + return (JSONObject) s.result(); + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/PathsRetainer.java b/json-smart-action/src/main/java/net/minidev/json/actions/PathsRetainer.java new file mode 100644 index 00000000..e641ec2e --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/PathsRetainer.java @@ -0,0 +1,85 @@ +package net.minidev.json.actions; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.DotDelimiter; +import net.minidev.json.actions.path.PathDelimiter; +import net.minidev.json.actions.traverse.JSONTraverseAction; +import net.minidev.json.actions.traverse.JSONTraverser; +import net.minidev.json.actions.traverse.LocatePathsJsonAction; +import net.minidev.json.actions.traverse.RetainPathsJsonAction; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Retains branches of nodes of a {@link JSONObject} matching the list of user-specified paths. + *

+ * A path to copy must be specified in the n-gram format - a list of keys from the root down separated by dots: + * K0[[[[.K1].K2].K3]...] + *
+ * A key to the right of a dot is a direct child of a key to the left of a dot. Keys with a dot in their name are + * not supported. + *

+ * Example: + *

+ * to retain the field k1.k2 in the {@link JSONObject} {k1:{k2:v1}, k3:{k4:v2}} instantiate the retainer like so: + * new JSONObjectCleaner("k1.k2") + * The resulting object would be {k1:{k2:v1}} + *

+ * See unit tests in JSONObjectRetainerTest for more examples + * + * @author adoneitan@gmail.com + */ +public class PathsRetainer { + protected List pathsToRetain; + protected PathDelimiter pathDelimiter = new DotDelimiter().withAcceptDelimiterInNodeName(false); + + public PathsRetainer(JSONArray pathsToRetain) { + if (pathsToRetain == null || pathsToRetain.isEmpty()) { + this.pathsToRetain = Collections.emptyList(); + } else { + this.pathsToRetain = new LinkedList(); + for (Object s : pathsToRetain) { + this.pathsToRetain.add((String) s); + } + } + } + + public PathsRetainer(List pathsToRetain) { + this.pathsToRetain = pathsToRetain == null || pathsToRetain.size() == 0 ? Collections. emptyList() : pathsToRetain; + } + + public PathsRetainer(String... pathsToRetain) { + this.pathsToRetain = pathsToRetain == null || pathsToRetain.length == 0 ? Collections. emptyList() + : new LinkedList(Arrays.asList(pathsToRetain)); + } + + public PathsRetainer with(PathDelimiter pathDelimiter) { + this.pathDelimiter = pathDelimiter; + return this; + } + + public JSONObject retain(JSONObject object) { + /** + * a path to retain which contains a path in the object, but is not itself a path in the object, + * will cause the sub-path to be retained although it shouldn't: + * object = {k0:v0} retain = {k0.k1} + * so the false path to retain has to be removed from the pathsToRetain list. + * + * The {@link LocatePathsJsonAction} returns only paths which exist in the object. + */ + JSONTraverseAction locateAction = new LocatePathsJsonAction(pathsToRetain, pathDelimiter); + JSONTraverser t1 = new JSONTraverser(locateAction); + t1.traverse(object); + List realPathsToRetain = (List) locateAction.result(); + + //now reduce the object using only existing paths + JSONTraverseAction retainer = new RetainPathsJsonAction(realPathsToRetain, pathDelimiter); + JSONTraverser t2 = new JSONTraverser(retainer).with(pathDelimiter); + t2.traverse(object); + return (JSONObject) retainer.result(); + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/navigate/CopyPathsAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/CopyPathsAction.java new file mode 100644 index 00000000..26e32a3b --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/CopyPathsAction.java @@ -0,0 +1,137 @@ +package net.minidev.json.actions.navigate; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.TreePath; + +import java.util.Collection; +import java.util.Stack; + +/** + * Creates a copy of a {@link JSONObject} containing just the nodes on the paths specified. + *

+ * Specified paths that do not exist in the source object are ignored silently. + * Specifying an empty list of paths to navigate or only non-existent paths will result in an empty + * object being returned. + *

+ * See package-info for more details + *

+ * Example: + *

+ * To copy the branch k1.k2 from {k1:{k2:v1}, k3:{k4:v2}} instantiate the copier like so: + * new JSONObjectCopier("k1.k2") + * The resulting copy would be {k1:{k2:v1}} + *

+ * See unit tests for more examples + * + * @author adoneitan@gmail.com + * @since 15 March 2016. + * + */ +public class CopyPathsAction implements JSONNavigateAction { + protected JSONObject destTree; + protected JSONObject destBranch; + protected Stack destNodeStack; + + @Override + public boolean start(JSONObject source, Collection pathsToCopy) { + if (source == null) { + destTree = null; + return false; + } + destTree = new JSONObject(); + if (pathsToCopy == null || pathsToCopy.size() == 0) { + return false; + } + return true; + } + + @Override + public boolean recurInto(TreePath jp, JSONObject o) { + //reached JSONObject node - instantiate it and recur + handleNewNode(jp, new JSONObject()); + return true; + } + + private void handleNewNode(TreePath jp, Object node) { + if (!jp.hasPrev()) { + return; + } + if (destNodeStack.peek() instanceof JSONObject) { + ((JSONObject) destNodeStack.peek()).put(jp.curr(), node); + } else if (destNodeStack.peek() instanceof JSONArray) { + ((JSONArray) destNodeStack.peek()).add(node); + } + destNodeStack.push(node); + } + + @Override + public boolean recurInto(TreePath jp, JSONArray o) { + //reached JSONArray node - instantiate it and recur + handleNewNode(jp, new JSONArray()); + return true; + } + + @Override + public void foundLeafBeforePathEnd(TreePath jp, Object obj) { + throw new IllegalArgumentException("branch is shorter than path - path not found in source: '" + jp.origin() + "'"); + } + + @Override + public void pathTailNotFound(TreePath jp, Object source) { + throw new IllegalArgumentException("cannot find next element of path - path not found in source: '" + jp.origin() + "'"); + } + + @Override + public void handleLeaf(TreePath jp, Object o) { + ((JSONObject) destNodeStack.peek()).put(jp.curr(), o); + } + + @Override + public void handleLeaf(TreePath jp, int arrIndex, Object o) { + ((JSONArray) destNodeStack.peek()).add(o); + } + + @Override + public void recurEnd(TreePath jp, JSONObject jo) { + destNodeStack.pop(); + } + + @Override + public void recurEnd(TreePath jp, JSONArray ja) { + destNodeStack.pop(); + } + + @Override + public boolean pathStart(String path) { + destBranch = new JSONObject(); + destNodeStack = new Stack(); + destNodeStack.push(destBranch); + return true; + } + + @Override + public void pathEnd(String path) { + destTree.merge(destBranch); + } + + @Override + public boolean failSilently(String path, Exception e) { + return false; + } + + @Override + public boolean failFast(String path, Exception e) { + return false; + } + + @Override + public void end() { + + } + + @Override + public Object result() { + return destTree; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/navigate/JSONNavigateAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/JSONNavigateAction.java new file mode 100644 index 00000000..c274e590 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/JSONNavigateAction.java @@ -0,0 +1,16 @@ +package net.minidev.json.actions.navigate; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +/** + * An interface for a processing action on the nodes of a {@link JSONObject} while navigating its branches. + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + * @since 15 June 2016. + */ +public interface JSONNavigateAction extends NavigateAction { + +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/navigate/JSONNavigator.java b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/JSONNavigator.java new file mode 100644 index 00000000..e2f60a24 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/JSONNavigator.java @@ -0,0 +1,21 @@ +package net.minidev.json.actions.navigate; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +import java.util.List; + +/** + * @author adoneitan@gmail.com + * @since 15 June 2016. + */ +public class JSONNavigator extends TreeNavigator { + + public JSONNavigator(JSONNavigateAction action, List pathsToNavigate) { + super(action, pathsToNavigate); + } + + public JSONNavigator(JSONNavigateAction action, String... pathsToNavigate) { + super(action, pathsToNavigate); + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/navigate/NavigateAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/NavigateAction.java new file mode 100644 index 00000000..3493cec1 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/NavigateAction.java @@ -0,0 +1,110 @@ +package net.minidev.json.actions.navigate; + +import net.minidev.json.actions.path.TreePath; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * An interface for a processing action on the nodes of a {@link M} while navigating its branches. + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + * @since 15 June 2016 + */ +public interface NavigateAction, L extends List> { + /** + * called before navigation of a new path starts + * @return true if the specified path should be navigated + */ + boolean pathStart(String path); + + /** + * called before any navigation of the {@link M} starts + * @return true if navigation should start at all + */ + boolean start(M objectToNavigate, Collection pathsToNavigate); + + /** + * reached end of branch in source before end of specified path - + * the specified path does not exist in the source + */ + void pathTailNotFound(TreePath tp, Object source); + + /** + * called after the navigation of a path ends + */ + void pathEnd(String path); + + /** + * called if navigation of a path throws an exception + * @return true if the failure on this path should not abort the rest of the navigation + */ + boolean failSilently(String path, Exception e); + + /** + * called if navigation of a path throws an exception + * @return true if the failure on this path should abort the rest of the navigation + */ + boolean failFast(String path, Exception e); + + /** + * called when an object node is encountered on the path + * @return true if the navigator should navigate into the object + */ + boolean recurInto(TreePath tp, M sourceNode); + + /** + * called when an array node is encountered on the path + * @return true if the navigator should navigate into the array + */ + boolean recurInto(TreePath tp, L sourceNode); + + /** + * reached leaf node (not a container) in source but specified path expects children - + * the specified path does not exist in the source + */ + void foundLeafBeforePathEnd(TreePath jp, Object obj); + + /** + * called when a leaf node is reached in a M. + * a leaf in a M is a key-value pair where the value is not a container itself + * (it is not a M nor a L) + * @param tp - the JsonPath pointing to the leaf + */ + void handleLeaf(TreePath tp, Object value); + + /** + * called when a leaf in a L is reached. + * a leaf in a L is a non-container item + * (it is not a M nor a L) + * @param arrIndex - the index of the item in the L + * @param arrItem - the item + */ + void handleLeaf(TreePath tp, int arrIndex, Object arrItem); + + /** + * called when navigation of an {@link M} type object ends + * @param tp the path pointing to the object + */ + void recurEnd(TreePath tp, M m); + + /** + * called when navigation of an {@link L} type object ends + * @param tp the path pointing to the object + */ + void recurEnd(TreePath tp, L l); + + /** + * called after all navigation ends, and just before the navigation method exits + */ + void end(); + + /** + * holds the result of the navigation, as assigned by the action implementing this interface + */ + Object result(); + +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/navigate/TreeNavigator.java b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/TreeNavigator.java new file mode 100644 index 00000000..7811d812 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/TreeNavigator.java @@ -0,0 +1,136 @@ +package net.minidev.json.actions.navigate; + +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.DotDelimiter; +import net.minidev.json.actions.path.TreePath; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Navigates only the branches of a {@link JSONObject} corresponding to the paths specified. + *

+ * For each specified path to navigate, the {@link TreeNavigator} only traverses the matching + * branch. + *

+ * The navigator accepts an action and provides callback hooks for it to act on the traversed + * nodes at each significant step. See {@link NavigateAction}. + *

+ * See package-info for more details + *

+ * Example: + *

+ * To navigate the branch k1.k2 of the object {"k1":{"k2":"v1"}, "k3":{"k4":"v2"}} instantiate + * the navigator like so: new JSONNavigator("k1.k2") + * + * @author adoneitan@gmail.com + * @since 15 June 2016. + * + */ +public class TreeNavigator, L extends List> { + protected List pathsToNavigate; + protected NavigateAction action; + protected String pathPrefix = ""; + + public TreeNavigator(NavigateAction action, List pathsToNavigate) { + if (action == null) { + throw new IllegalArgumentException("NavigateAction cannot be null"); + } + this.action = action; + this.pathsToNavigate = pathsToNavigate; + } + + public TreeNavigator with(String pathPrefix) { + this.pathPrefix = pathPrefix; + return this; + } + + public TreeNavigator(NavigateAction action, String... pathsToNavigate) { + this(action, Arrays.asList(pathsToNavigate)); + } + + public void nav(M object) throws Exception { + if (action.start(object, pathsToNavigate)) { + for (String path : pathsToNavigate) { + try { + if (path != null && !path.equals("") && action.pathStart(path)) { + TreePath jp = new TreePath(path, new DotDelimiter().withAcceptDelimiterInNodeName(true)); + nav(jp, object); + action.pathEnd(path); + } + } catch (Exception e) { + if (action.failSilently(path, e)) { + break; + } else if (action.failFast(path, e)) { + throw e; + } + } + } + } + action.end(); + } + + public void nav(TreePath jp, M map) { + if (map == null || !action.recurInto(jp, map)) { + //source is null - navigation impossible + return; + } + + if (jp.hasNext()) { + String key = jp.next(); + if (!map.containsKey(key)) { + // cannot find next element of path in the source - + // the specified path does not exist in the source + action.pathTailNotFound(jp, map); + } else if (map.get(key) instanceof Map) { + //reached Map type node - handle it and recur into it + nav(jp, (M) map.get(key)); + } else if (map.get(key) instanceof List) { + //reached List type node - handle it and recur into it + nav(jp, (L) map.get(key)); + } else if (jp.hasNext()) { + // reached leaf node (not a container) in source but specified path expects children - + // the specified path is illegal because it does not exist in the source. + action.foundLeafBeforePathEnd(jp, map.get(key)); + } else if (!jp.hasNext()) { + //reached leaf in source and specified path is also at leaf -> handle it + action.handleLeaf(jp, map.get(key)); + } else { + throw new IllegalStateException("fatal: unreachable code reached at '" + jp.origin() + "'"); + } + } + action.recurEnd(jp, (M) map); + } + + public void nav(TreePath jp, L list) { + if (list == null || !action.recurInto(jp, (L) list)) { + //list is null - navigation impossible + return; + } + int arrIndex = 0; + for (Object arrItem : list.toArray()) { + if (arrItem instanceof Map) { + // clone the path so that for each object in the array, + // the iterator continues from the same position in the path + TreePath jpClone = getClone(jp); + nav(jpClone, (M) arrItem); + } else if (arrItem instanceof List) { + nav(jp, (L) arrItem); + } else if (!jp.hasNext()) { + //reached leaf - handle it + action.handleLeaf(jp, arrIndex, arrItem); + } + arrIndex++; + } + action.recurEnd(jp, (L) list); + } + + private TreePath getClone(TreePath jp) { + try { + return jp.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("failed to clone path", e); + } + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/navigate/package-info.java b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/package-info.java new file mode 100644 index 00000000..40727e40 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/navigate/package-info.java @@ -0,0 +1,28 @@ +/** + * Navigate user-specified paths in a tree made up of {@link java.util.Map}s and {@link java.util.List} and process them + *

+ * {@link net.minidev.json.actions.navigate.TreeNavigator} only navigates through branches corresponding + * to user-specified paths. For each path, the navigation starts at the root and moves down the branch. + *

+ * The {@link net.minidev.json.actions.navigate.TreeNavigator} accepts a + * {@link net.minidev.json.actions.navigate.NavigateAction} and provides callback hooks at each significant + * step which the {@link net.minidev.json.actions.navigate.NavigateAction} may use to process + * the nodes. + *

+ * A path to navigate must be specified in the n-gram format - a list of keys from the root down separated by dots: + * K0[[[[.K1].K2].K3]...] + *
+ * A key to the right of a dot is a direct child of a key to the left of a dot. Keys with a dot in their name are + * not supported. + *

+ * Sample usage: + *

+ * NavigateAction navAction = new NavigateAction(){...};
+ * JSONNavigator jsonNav = new JSONNavigator(navAction, "foo.bar.path");
+ * jsonNav.nav(new JSONObject(...));
+ * Object result = navAction.result();
+ * 
+ * + * @author adoneitan@gmail.com + */ +package net.minidev.json.actions.navigate; \ No newline at end of file diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/path/DotDelimiter.java b/json-smart-action/src/main/java/net/minidev/json/actions/path/DotDelimiter.java new file mode 100644 index 00000000..e20a1f2b --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/path/DotDelimiter.java @@ -0,0 +1,20 @@ +package net.minidev.json.actions.path; + +/** + * Encapsulates the delimiter '.' of the path parts when the path is specified in n-gram format. + * For example: root.node1.node11.leaf + * + * @author adoneitan@gmail.com + * @since 31 May2016 + */ +public class DotDelimiter extends PathDelimiter { + protected static final char DELIM_CHAR = '.'; + + public DotDelimiter() { + super(DELIM_CHAR); + } + + public String regex() { + return "\\."; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/path/PathDelimiter.java b/json-smart-action/src/main/java/net/minidev/json/actions/path/PathDelimiter.java new file mode 100644 index 00000000..b22fea88 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/path/PathDelimiter.java @@ -0,0 +1,40 @@ +package net.minidev.json.actions.path; + +/** + * Encapsulates the delimiter of the path parts when given in n-gram format. + * + * @author adoneitan@gmail.com + * @since 31 May 2016 + */ +public abstract class PathDelimiter { + protected char delimChar; + protected String delimStr; + protected boolean acceptDelimInKey; + + public PathDelimiter(char delim) { + this.delimChar = delim; + this.delimStr = String.valueOf(delim); + } + + public PathDelimiter withAcceptDelimiterInNodeName(boolean acceptDelimInKey) { + this.acceptDelimInKey = acceptDelimInKey; + return this; + } + + public boolean accept(String key) { + if (!acceptDelimInKey && key.contains(delimStr)) + return false; + return true; + } + + public String str() { + return delimStr; + } + + public char chr() { + return delimChar; + } + + public abstract String regex(); + +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/path/SlashDelimiter.java b/json-smart-action/src/main/java/net/minidev/json/actions/path/SlashDelimiter.java new file mode 100644 index 00000000..7a341a90 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/path/SlashDelimiter.java @@ -0,0 +1,21 @@ +package net.minidev.json.actions.path; + +/** + * Encapsulates the delimiter '.' of the path parts when the path is specified in n-gram format. + * For example: root.node1.node11.leaf + * + * @author adoneitan@gmail.com + * @since 31 May 2016 + */ +public class SlashDelimiter extends PathDelimiter { + protected static final char DELIM_CHAR = '/'; + + public SlashDelimiter() { + super(DELIM_CHAR); + super.withAcceptDelimiterInNodeName(false); + } + + public String regex() { + return "\\/"; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/path/TreePath.java b/json-smart-action/src/main/java/net/minidev/json/actions/path/TreePath.java new file mode 100644 index 00000000..64d996e1 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/path/TreePath.java @@ -0,0 +1,232 @@ +package net.minidev.json.actions.path; + +import java.util.Arrays; +import java.util.Map; +import java.util.List; +import java.util.ListIterator; + +/** + * {@link TreePath} represents an n-gram formatted path + * corresponding to a branch in a tree of {@link Map}s + * and {@link List}s + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + */ +public class TreePath { + + protected enum Step { + NONE, NEXT, PREV + } + + protected final String path; + protected List keys; + protected ListIterator keysItr; + protected String currKey; + protected Step lastStep; + protected StringBuilder origin; + protected StringBuilder remainder; + protected PathDelimiter delim; + + public TreePath(String path, PathDelimiter delim) { + this.delim = delim; + checkPath(path); + this.path = path; + this.keys = Arrays.asList(path.split(delim.regex())); + reset(); + } + + public void reset() { + keysItr = keys.listIterator(); + currKey = ""; + lastStep = Step.NONE; + origin = new StringBuilder(""); + remainder = new StringBuilder(path); + } + + public boolean hasNext() { + return keysItr.hasNext(); + } + + public int nextIndex() { + return keysItr.nextIndex(); + } + + public String next() { + currKey = keysItr.next(); + /** when changing direction the {@link ListIterator} does not really + * move backward so an extra step is performed */ + if (!lastStep.equals(Step.PREV)) { + originIncrement(); + remainderDecrement(); + } + lastStep = Step.NEXT; + return currKey; + } + + public boolean hasPrev() { + return keysItr.hasPrevious(); + } + + public int prevIndex() { + return keysItr.previousIndex(); + } + + public String prev() { + String temp = currKey; + currKey = keysItr.previous(); + /** when changing direction the {@link ListIterator} does not really + * move backward so an extra step is performed */ + if (!lastStep.equals(Step.NEXT)) { + remainderIncrement(temp); + originDecrement(); + } + lastStep = Step.PREV; + return currKey; + } + + private void remainderDecrement() { + if (length() == 1) + remainder = new StringBuilder(""); + else if (remainder.indexOf(delim.str()) < 0) + remainder = new StringBuilder(""); + else + remainder.delete(0, remainder.indexOf(delim.str()) + 1); + } + + private void originDecrement() { + if (length() == 1) + origin = new StringBuilder(""); + else if (origin.indexOf(delim.str()) < 0) + origin = new StringBuilder(""); + else + origin.delete(origin.lastIndexOf(delim.str()), origin.length()); + } + + private void originIncrement() { + if (origin.length() != 0) { + origin.append(delim.chr()); + } + origin.append(currKey); + } + + private void remainderIncrement(String prev) { + if (remainder.length() == 0) + remainder = new StringBuilder(prev); + else + remainder = new StringBuilder(prev).append(delim.chr()).append(remainder); + } + + /** + * @return An n-gram path from the first key to the current key (inclusive) + */ + public String path() { + return path; + } + + /** + * @return An n-gram path from the first key to the current key (inclusive) + */ + public String origin() { + return origin.toString(); + } + + /** + * @return An n-gram path from the current key to the last key (inclusive) + */ + public String remainder() { + return remainder.toString(); + } + + /** + * @return first element in the JSONPath + */ + public String first() { + return keys.get(0); + } + + /** + * @return last element in the JSONPath + */ + public String last() { + return keys.get(keys.size() - 1); + } + + /** + * @return current element pointed to by the path iterator + */ + public String curr() { + return currKey; + } + + public int length() { + return keys.size(); + } + + public String subPath(int firstIndex, int lastIndex) { + if (lastIndex < firstIndex) { + throw new IllegalArgumentException("bad call to subPath"); + } + StringBuilder sb = new StringBuilder(path.length()); + for (int i = firstIndex; i <= lastIndex; i++) { + sb.append(keys.get(i)); + if (i < lastIndex) { + sb.append(delim.chr()); + } + } + sb.trimToSize(); + return sb.toString(); + } + + private void checkPath(String path) { + if (path == null || path.equals("")) + throw new IllegalArgumentException("path cannot be null or empty"); + if (path.startsWith(delim.str()) || path.endsWith(delim.str()) || path.contains(delim.str() + delim.str())) + throw new IllegalArgumentException(String.format("path cannot start or end with %s or contain '%s%s'", delim.str(), delim.str(), delim.str())); + } + + @Override + public TreePath clone() throws CloneNotSupportedException { + TreePath cloned = new TreePath(this.path, this.delim); + while (cloned.nextIndex() != this.nextIndex()) { + cloned.next(); + } + if (cloned.prevIndex() != this.prevIndex()) { + cloned.prev(); + } + cloned.lastStep = this.lastStep; + cloned.currKey = new String(this.currKey); + cloned.origin = new StringBuilder(this.origin); + cloned.remainder = new StringBuilder(this.remainder); + return cloned; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + TreePath treePath = (TreePath) o; + + return path().equals(treePath.path()) && hasNext() == treePath.hasNext() && hasPrev() == treePath.hasPrev() && curr().equals(treePath.curr()) + && origin().equals(treePath.origin()) && remainder().equals(treePath.remainder()) && lastStep == treePath.lastStep + && delim.equals(treePath.delim); + + } + + @Override + public int hashCode() { + int result = path.hashCode(); + result = 31 * result + keys.hashCode(); + result = 31 * result + keysItr.hashCode(); + result = 31 * result + currKey.hashCode(); + result = 31 * result + lastStep.hashCode(); + result = 31 * result + origin.hashCode(); + result = 31 * result + remainder.hashCode(); + result = 31 * result + delim.hashCode(); + return result; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/JSONTraverseAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/JSONTraverseAction.java new file mode 100644 index 00000000..11c6151b --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/JSONTraverseAction.java @@ -0,0 +1,14 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +/** + * An interface for a processing action on the nodes of a {@link JSONObject} while traversing it. + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + */ +public interface JSONTraverseAction extends TreeTraverseAction { +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/JSONTraverser.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/JSONTraverser.java new file mode 100644 index 00000000..506526cb --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/JSONTraverser.java @@ -0,0 +1,31 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.DotDelimiter; +import net.minidev.json.actions.path.PathDelimiter; + +/** + * Traverses every node of a {@link JSONObject} + *

+ * {@link JSONTraverser} accepts an action and provides callback hooks for it to act + * on the traversed nodes at each significant step. See {@link JSONTraverseAction}. + *

+ * A key to the right of a dot is a direct child of a key to the left of a dot. + * Keys with a dot in their name are not supported. + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + * + */ +public class JSONTraverser extends TreeTraverser { + public JSONTraverser(JSONTraverseAction action) { + super(action, new DotDelimiter()); + } + + public JSONTraverser with(PathDelimiter delim) { + super.delim = delim; + return this; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/KeysPrintAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/KeysPrintAction.java new file mode 100644 index 00000000..ad0e10ea --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/KeysPrintAction.java @@ -0,0 +1,59 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +import java.util.Map.Entry; + +/** + * @author adoneitan@gmail.com + * @since 5/24/16. + */ +public class KeysPrintAction implements JSONTraverseAction { + @Override + public boolean start(JSONObject object) { + return true; + } + + @Override + public boolean traverseEntry(String fullPathToEntry, Entry entry) { + System.out.println(entry.getKey()); + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONObject entryValue) { + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONArray entryValue) { + return true; + } + + @Override + public void handleLeaf(String pathToEntry, Entry entry) { + + } + + @Override + public void handleLeaf(String fullPathToContainingList, int listIndex, Object listItem) { + + } + + @Override + public boolean removeEntry(String fullPathToEntry, Entry entry) { + return false; + } + + @Override + public void end() { + + } + + @Override + public Object result() { + return null; + } + +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/LocatePathsJsonAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/LocatePathsJsonAction.java new file mode 100644 index 00000000..2c8b704d --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/LocatePathsJsonAction.java @@ -0,0 +1,95 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.PathDelimiter; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + +/** + * Searches for paths in a {@link JSONObject} and returns those found + *

+ * A path is not removed from the user-specified list once its processing is over, + * because identical objects in the same array are supported by this action. + *

+ * See package-info for more details + *

+ * See unit tests for examples + * + * @author adoneitan@gmail.com + * + */ +public class LocatePathsJsonAction implements JSONTraverseAction { + protected List pathsFound; + protected List pathsToFind; + protected PathDelimiter delim; + + /** + * + * @param pathsToFind + * A path to a field in the {@link JSONObject} should be specified in n-gram format where keys are chained: k0[[[.k1].k2]...] + */ + public LocatePathsJsonAction(List pathsToFind, PathDelimiter delim) { + this.pathsToFind = pathsToFind; + this.delim = delim; + pathsFound = new LinkedList(); + } + + @Override + public boolean start(JSONObject object) { + return object != null && pathsToFind != null && pathsToFind.size() > 0; + } + + @Override + public boolean traverseEntry(String fullPathToEntry, Entry entry) { + if (!delim.accept(entry.getKey())) { + return false; + } + locatePath(fullPathToEntry); + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONObject entryValue) { + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONArray entryValue) { + return true; + } + + @Override + public void handleLeaf(String pathToEntry, Entry entry) { + + } + + @Override + public void handleLeaf(String fullPathToContainingList, int listIndex, Object listItem) { + + } + + @Override + public boolean removeEntry(String fullPathToEntry, Entry entry) { + return false; + } + + @Override + public void end() { + // nothing to do + } + + @Override + public Object result() { + return pathsFound; + } + + private void locatePath(String pathToEntry) { + if (pathsToFind.contains(pathToEntry)) { + // reached end of path that is being searched + pathsFound.add(pathToEntry); + } + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RemoveElementsJsonAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RemoveElementsJsonAction.java new file mode 100644 index 00000000..9965d720 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RemoveElementsJsonAction.java @@ -0,0 +1,81 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +import java.util.Map; +import java.util.Map.Entry; + +/** + * Removes key:value elements from a {@link JSONObject}. + *

+ * An element is not removed from the user-specified list once its processing is over, + * because it may appear in more than one node. + *

+ * See package-info for more details + *

+ * See unit tests for examples + * + * @author adoneitan@gmail.com + * + */ +public class RemoveElementsJsonAction implements JSONTraverseAction { + protected JSONObject result; + protected final Map elementsToRemove; + protected final boolean allowDotChar; + + public RemoveElementsJsonAction(Map elementsToRemove, boolean allowDotChar) { + this.elementsToRemove = elementsToRemove; + this.allowDotChar = allowDotChar; + } + + public RemoveElementsJsonAction(Map elementsToRemove) { + this(elementsToRemove, false); + } + + @Override + public boolean start(JSONObject object) { + result = object; + return object != null && elementsToRemove != null && elementsToRemove.size() > 0; + } + + @Override + public boolean removeEntry(String fullPathToEntry, Entry entry) { + return elementsToRemove.entrySet().contains(entry); + } + + @Override + public boolean traverseEntry(String fullPathToEntry, Entry entry) { + // must traverse the whole object + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONObject entryValue) { + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONArray entryValue) { + return true; + } + + @Override + public void handleLeaf(String pathToEntry, Entry entry) { + } + + @Override + public void handleLeaf(String fullPathToContainingList, int listIndex, Object listItem) { + + } + + @Override + public void end() { + // nothing to do + } + + @Override + public Object result() { + return result; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RemovePathsJsonAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RemovePathsJsonAction.java new file mode 100644 index 00000000..e5983afd --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RemovePathsJsonAction.java @@ -0,0 +1,76 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; + +import java.util.List; +import java.util.Map.Entry; + +/** + * Removes branches from a {@link JSONObject}. + *

+ * A path is not removed from the user-specified list once its processing is over, + * because identical objects in the same array are supported by this action. + *

+ * See package-info for more details + *

+ * See unit tests for examples + * + * @author adoneitan@gmail.com + * + */ +public class RemovePathsJsonAction implements JSONTraverseAction { + protected JSONObject result; + protected List pathsToRemove; + + public RemovePathsJsonAction(List pathsToRemove) { + this.pathsToRemove = pathsToRemove; + } + + @Override + public boolean start(JSONObject object) { + result = object; + return object != null && pathsToRemove != null && pathsToRemove.size() > 0; + } + + @Override + public boolean removeEntry(String fullPathToEntry, Entry entry) { + return pathsToRemove.contains(fullPathToEntry); + } + + @Override + public boolean traverseEntry(String fullPathToEntry, Entry entry) { + // must traverse the whole object + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONObject entryValue) { + return true; + } + + @Override + public boolean recurInto(String pathToEntry, JSONArray entryValue) { + return true; + } + + @Override + public void handleLeaf(String pathToEntry, Entry entry) { + + } + + @Override + public void handleLeaf(String fullPathToContainingList, int listIndex, Object listItem) { + + } + + @Override + public void end() { + // nothing to do + } + + @Override + public Object result() { + return result; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RetainPathsJsonAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RetainPathsJsonAction.java new file mode 100644 index 00000000..1948da93 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/RetainPathsJsonAction.java @@ -0,0 +1,119 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.actions.path.PathDelimiter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +/** + * Retain branches or parts of branches matching a specified list of paths. + *

+ * Paths are matched from the root down. If a user-specified path ends at a non-leaf node, + * the rest of the branch from that node to the leaf is not retained. + *

+ * A path is not removed from the user-specified list once its processing is over, + * because identical objects in the same array are supported by this action. + *

+ * See package-info for more details + *

+ * See unit tests for examples + * + * @author adoneitan@gmail.com + * + */ +public class RetainPathsJsonAction implements JSONTraverseAction +{ + protected final PathDelimiter delim; + protected JSONObject result; + protected List pathsToRetain; + + public RetainPathsJsonAction(List pathsToRetain, PathDelimiter delim) + { + this.pathsToRetain = new ArrayList(pathsToRetain); + this.delim = delim; + } + + @Override + public boolean start(JSONObject object) + { + if (object == null) + { + result = null; + return false; + } + if (pathsToRetain == null || pathsToRetain.size() == 0) + { + result = new JSONObject(); + return false; + } + result = object; + return true; + } + + @Override + public boolean traverseEntry(String fullPathToEntry, Entry entry) { + return true; + } + + @Override + public boolean recurInto(String fullPathToSubtree, JSONObject entryValue) { + return true; + } + + @Override + public boolean recurInto(String fullPathToArrayItem, JSONArray entryValue) { + return true; + } + + @Override + public void handleLeaf(String pathToEntry, Entry entry) { + } + + @Override + public void handleLeaf(String fullPathToContainingList, int listIndex, Object listItem) { + } + + @Override + public boolean removeEntry(String fullPathToEntry, Entry entry) { + return discardPath(fullPathToEntry, entry); + } + + @Override + public void end() + { + // nothing to do + } + + @Override + public Object result() { + return result; + } + + /** + * if the full path to the entry is not contained in any of the paths to retain - remove it from the object + * this step does not remove entries whose full path is contained in a path to retain but are not equal to an + * entry to retain + */ + protected boolean discardPath(String pathToEntry, Entry entry) + { + if (!foundAsPrefix(pathToEntry) || !delim.accept(entry.getKey())) + { + //skip traversal of subtree and remove from the traversal iterator + return true; + } + return false; + } + + protected boolean foundAsPrefix(String path) + { + for (String p : pathsToRetain) { + if (p == path || (p != null && path != null && p.startsWith(path))) { + return true; + } + } + return false; + } +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/TreeTraverseAction.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/TreeTraverseAction.java new file mode 100644 index 00000000..eaa52528 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/TreeTraverseAction.java @@ -0,0 +1,72 @@ +package net.minidev.json.actions.traverse; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * An interface for a processing action on the nodes of a {@link M} tree + * while traversing it. The order in which the callbacks are listed + * below is the order in which they are called by the {@link TreeTraverser} + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + */ +public interface TreeTraverseAction, L extends List> { + /** + * called before any traversal of the {@link M} tree starts + * @return true if traversal should start at all + */ + boolean start(M object); + + /** + * called when a new entry is encountered and before any processing is performed on it + * @return true if the entry should be processed + */ + boolean traverseEntry(String fullPathToEntry, Entry entry); + + /** + * the last callback for each entry in an {@link M} map. if this method returns true + * the {@link TreeTraverser} removes the entry from the map. there is no further + * handling of the entry. + * @return true if the entry and its subtree should be removed from the {@link M} tree + */ + boolean removeEntry(String fullPathToEntry, Entry entry); + + /** + * called when a non-leaf entry is encountered inside an {@Link M} object + * @return true if the non-leaf entry should be recursively traversed + */ + boolean recurInto(String fullPathToSubtree, M entryValue); + + /** + * called when a non-leaf item is encountered inside an {@Link L} object + * @return true if the non-leaf item should be recursively traversed + */ + boolean recurInto(String fullPathToContainingList, L entryValue); + + /** + * called for each leaf of an {@link M} map is encountered + */ + void handleLeaf(String fullPathToEntry, Entry entry); + + /** + * called for each leaf of an {@link L} list is encountered + * @param listItem - the item + * @param listIndex - the ordered location of the item in the list + */ + void handleLeaf(String fullPathToContainingList, int listIndex, Object listItem); + + /** + * called after the traversal ends, + * and just before the {@link #start(M)} method exits + */ + void end(); + + /** + * holds the result of the traversal, + * as assigned by the action implementing this interface + */ + Object result(); +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/TreeTraverser.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/TreeTraverser.java new file mode 100644 index 00000000..77f9085f --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/TreeTraverser.java @@ -0,0 +1,90 @@ +package net.minidev.json.actions.traverse; + +import net.minidev.json.actions.path.PathDelimiter; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Traverses every node of a tree made up of a combination of {@link Map}s and {@link List}s + *

+ * {@link TreeTraverser} accepts an action and provides callback hooks for it to act + * on the traversed nodes at each significant step. See {@link TreeTraverseAction}. + *

+ * See package-info for more details + * + * @author adoneitan@gmail.com + * + */ +public class TreeTraverser, L extends List> { + protected TreeTraverseAction action; + protected PathDelimiter delim; + protected String pathPrefix = ""; + + public TreeTraverser(TreeTraverseAction action, PathDelimiter delim) { + this.action = action; + this.delim = delim; + } + + public TreeTraverser with(String pathPrefix) { + this.pathPrefix = pathPrefix; + return this; + } + + public void traverse(M map) { + if (action.start(map)) { + depthFirst(pathPrefix, map); + } + action.end(); + } + + private void depthFirst(String fullPath, M map) { + if (map == null || map.entrySet() == null || !action.recurInto(fullPath, map)) { + return; + } + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Entry entry = it.next(); + String fullPathToEntry = buildPath(fullPath, entry.getKey()); + + if (!action.traverseEntry(fullPathToEntry, entry)) { + continue; + } else if (action.removeEntry(fullPathToEntry, entry)) { + it.remove(); + continue; + } + + if (entry.getValue() instanceof Map) { + depthFirst(fullPathToEntry, (M) entry.getValue()); + } else if (entry.getValue() instanceof List) { + depthFirst(fullPathToEntry, (L) entry.getValue()); + } else { + action.handleLeaf(fullPathToEntry, entry); + } + } + } + + private void depthFirst(String fullPath, L list) { + if (!action.recurInto(fullPath, (L) list)) { + return; + } + int listIndex = 0; + for (Object listItem : list.toArray()) { + if (listItem instanceof Map) { + depthFirst(fullPath, (M) listItem); + } else if (listItem instanceof List) { + depthFirst(fullPath, (L) listItem); + } else { + action.handleLeaf(fullPath, listIndex, listItem); + } + listIndex++; + } + } + + private String buildPath(String fullPath, String entryKey) { + return pathPrefix.equals(fullPath) ? pathPrefix + entryKey : fullPath + delim.str() + entryKey; + } + +} diff --git a/json-smart-action/src/main/java/net/minidev/json/actions/traverse/package-info.java b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/package-info.java new file mode 100644 index 00000000..d6c549b7 --- /dev/null +++ b/json-smart-action/src/main/java/net/minidev/json/actions/traverse/package-info.java @@ -0,0 +1,30 @@ +/** + * + * Traverse all the nodes in a {@link net.minidev.json.JSONObject} and process them + *

+ * The traversal starts at the root and moves breadth-first down the branches. + * The {@link net.minidev.json.actions.traverse.TreeTraverser} accepts a + * {@link net.minidev.json.actions.traverse.JSONTraverseAction} and provides callback hooks at each significant + * step which the {@link net.minidev.json.actions.traverse.JSONTraverseAction} may use to process + * the nodes. + *

+ * The {@link net.minidev.json.actions.traverse.TreeTraverser} assumes that paths in the tree which the + * {@link net.minidev.json.JSONObject} represents are specified in the n-gram format - a list of keys from the + * root down separated by dots: + *

+ * K0[[[[.K1].K2].K3]...] + *

+ * A key to the right of a dot is a direct child of a key to the left of a dot. + * Keys with a dot in their name are not supported. + *

+ * Sample usage: + *

+ * TraverseAction tAction = new TraverseAction(){...};
+ * JSONTraverser jsonTrv = new JSONTraverser(tAction);
+ * jsonTrv.traverse(new JSONObject(...));
+ * Object result = tAction.result();
+ * 
+ * + * @author adoneitan@gmail.com + */ +package net.minidev.json.actions.traverse; \ No newline at end of file diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/ElementRemoverTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/ElementRemoverTest.java new file mode 100644 index 00000000..c71aeac4 --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/ElementRemoverTest.java @@ -0,0 +1,107 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import net.minidev.json.actions.ElementRemover; +import net.minidev.json.parser.ParseException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.*; + +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link ElementRemover} + * + * @author adoneitan@gmail.com + */ +@RunWith(Parameterized.class) +public class ElementRemoverTest { + private String jsonToClean; + private String elementsToRemove; + private String expectedJson; + + public ElementRemoverTest(String jsonToClean, String elementsToRemove, String expectedJson) { + this.jsonToClean = filter(jsonToClean); + this.elementsToRemove = filter(elementsToRemove); + this.expectedJson = filter(expectedJson); + } + + private static String filter(String test) { + if (test == null) + return null; + return test.replace("'", "\""); + } + + @Parameterized.Parameters + public static Collection params() { + List list = Arrays.asList(new String[][]{ + {"{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}", null, "{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}"}, + {"{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}", "{}", "{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}"}, + {"{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}", "{'k0':'v2'}", "{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}"}, + {"{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}", "{'k2':'v2'}", "{'k0':{},'k1':{'k3':'v3'}}"}, + {"{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}", "{'k0':{'k2':'v2'}}", "{'k1':{'k2':'v2','k3':'v3'}}"}, + {"{'k0':{'k2':'v2'},'k1':{'k2':'v2','k3':'v3'}}", "{'k2':'v2','k3':'v3'}", "{'k0':{},'k1':{}}"}, + {"{'k0':{}}", "{}", "{'k0':{}}"}, + }); + return list; + } + + @Test + public void test() throws ParseException { + JSONObject objectToClean = jsonToClean != null ? (JSONObject) JSONValue.parseWithException(jsonToClean) : null; + JSONObject expectedObject = expectedJson != null ? (JSONObject) JSONValue.parseWithException(expectedJson) : null; + JSONObject toRemove = elementsToRemove != null ? (JSONObject) JSONValue.parseWithException(elementsToRemove) : null; + ElementRemover er = new ElementRemover(toRemove); + er.remove(objectToClean); + assertEquals(expectedObject, objectToClean); + } + + // private ElementRemover switchKeyToRemove() + // { + // long m = System.currentTimeMillis(); + // if (elementsToRemove == null && m % 4 == 0) + // { + // System.out.println("cast to String"); + // return new ElementRemover((String)null); + // } + // else if (elementsToRemove == null && m % 4 == 1) + // { + // System.out.println("cast to String[]"); + // return new ElementRemover((String[])null); + // } + // else if (elementsToRemove == null && m % 4 == 2) + // { + // System.out.println("cast to JSONArray"); + // return new ElementRemover((JSONArray)null); + // } + // else if (elementsToRemove == null && m % 4 == 3) + // { + // System.out.println("cast to List"); + // return new ElementRemover((List)null); + // } + // else if (elementsToRemove instanceof String) + // { + // return new ElementRemover((String) elementsToRemove); + // } + // else if (elementsToRemove instanceof String[]) + // { + // return new ElementRemover((String[]) elementsToRemove); + // } + // else if (elementsToRemove instanceof JSONArray) + // { + // return new ElementRemover((JSONArray) elementsToRemove); + // } + // else if (elementsToRemove instanceof List) + // { + // return new ElementRemover((List) elementsToRemove); + // } + // else + // { + // throw new IllegalArgumentException("bad test setup: wrong type of key to remove"); + // } + // } + +} diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/KeysPrintActionTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/KeysPrintActionTest.java new file mode 100644 index 00000000..20f5512b --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/KeysPrintActionTest.java @@ -0,0 +1,43 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import net.minidev.json.actions.traverse.JSONTraverser; +import net.minidev.json.actions.traverse.KeysPrintAction; +import net.minidev.json.parser.ParseException; +import org.junit.Test; + +/** + * @author adoneitan@gmail.com + * @since 30 May 2016 + */ +public class KeysPrintActionTest { + @Test + public void test() throws ParseException { + KeysPrintAction p = new KeysPrintAction(); + JSONTraverser t = new JSONTraverser(p); + + String data ="{" + + "'k0':{" + + "'k01':{" + + "'k011':'v2'" + + "}" + + "}," + + "'k1':{" + + "'k11':{" + + "'k111':'v5'" + + "}," + + "'k12':{" + + "'k121':'v5'" + + "}" + + "}," + + "'k3':{" + + "'k31':{" + + "'k311':'v5'" + + "}" + + "}" + + "}"; + JSONObject jo = (JSONObject) JSONValue.parseWithException(data.replace("'", "\"")); + t.traverse(jo); + } +} \ No newline at end of file diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/PathLocatorTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathLocatorTest.java new file mode 100644 index 00000000..ba904c34 --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathLocatorTest.java @@ -0,0 +1,141 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.actions.PathLocator; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import net.minidev.json.parser.ParseException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author adoneitan@gmail.com + */ +@RunWith(Parameterized.class) +public class PathLocatorTest +{ + private String jsonToSearch; + private Object keysToFind; + private String[] expectedFound; + + public PathLocatorTest(String jsonToSearch, Object keysToFind, String[] expectedFound) + { + this.jsonToSearch = jsonToSearch; + this.keysToFind = keysToFind; + this.expectedFound = expectedFound; + } + + @Parameterized.Parameters + public static Collection params() { + return Arrays.asList(new Object[][]{ + + //nulls, bad/empty keys + {null, null, new String[]{} }, + {null, "", new String[]{} }, + {null, "k1", new String[]{} }, + {null, new String[]{}, new String[]{} }, + {null, new JSONArray(), new String[]{} }, + {null, new ArrayList(0), new String[]{} },//5 + + //empty json, bad/empty keys + {"{}", null, new String[]{} }, + {"{}", "", new String[]{} }, + {"{}", "k1", new String[]{} }, + {"{}", new String[]{}, new String[]{} }, + {"{}", new JSONArray(), new String[]{} },//10 + {"{}", new ArrayList(0), new String[]{} }, + + //simple json, bad/empty keys + {"{\"k0\":\"v0\"}", null, new String[]{} }, + {"{\"k0\":\"v0\"}", "", new String[]{} }, + {"{\"k0\":\"v0\"}", "k1", new String[]{} }, + {"{\"k0\":\"v0\"}", new String[]{}, new String[]{} },//15 + {"{\"k0\":\"v0\"}", new JSONArray(), new String[]{} }, + {"{\"k0\":\"v0\"}", new ArrayList(0), new String[]{} }, + + //simple json, valid/invalid keys + {"{\"k0\":\"v0\"}", "k0", new String[]{"k0"} }, + {"{\"k0\":\"v0\"}", "v0", new String[]{} }, + {"{\"k0\":\"v0\"}", "k0.k1", new String[]{} },//20 + {"{\"k0\":\"v0\"}", "k1.k0", new String[]{} }, + {"{\"k0\":null}", "k0", new String[]{"k0"} }, + {"{\"k0\":null}", null, new String[]{} }, + + //key with dot char + {"{\"k0.k1\":\"v0\"}", "k0", new String[]{} }, + {"{\"k0.k1\":\"v0\"}", "k1", new String[]{} },//25 + {"{\"k0.k1\":\"v0\"}", "k0.k1", new String[]{} }, + + // key with dot ambiguity + {"{\"k0.k1\":\"withDot\",\"k0\":{\"k1\":null}}", "k0", new String[]{"k0"} }, + {"{\"k0.k1\":\"withDot\",\"k0\":{\"k1\":null}}", "k1", new String[]{} }, + {"{\"k0.k1\":\"withDot\",\"k0\":{\"k1\":null}}", "k0.k1", new String[]{"k0.k1"} }, + {"{\"k0\":{\"k1.k2\":\"dot\",\"k1\":{\"k2\":null}}}", "k0.k1", new String[]{"k0.k1"} },//30 + {"{\"k0\":{\"k1.k2\":\"dot\",\"k1\":{\"k2\":null}}}", "k0.k1.k2", new String[]{"k0.k1.k2"} }, + {"{\"k0\":{\"k1.k2\":\"dot\",\"k1\":{\"k2\":null}}}", "k1.k2", new String[]{} }, + {"{\"k0\":{\"k1.k2\":\"dot\"},\"k1\":{\"k2\":\"v2\"}}}","k0", new String[]{"k0"} }, + {"{\"k0\":{\"k1.k2\":\"dot\"},\"k1\":{\"k2\":\"v2\"}}}","k1.k2", new String[]{"k1.k2"} }, + + //ignore non-existent keys but keep good keys + {"{\"k0\":\"v0\",\"k1\":\"v1\"}", new String[]{"k0","k2"}, new String[]{"k0"} },//35 + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k2"}, new String[]{"k0"} }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1.k2"}, new String[]{"k0", "k1.k2"} }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1.k2.k3"}, new String[]{"k0"} }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1.k2","k1"}, new String[]{"k0","k1","k1.k2"} }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1","k0.k2"}, new String[]{"k0","k1"} },//40 + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1","k2"}, new String[]{"k0","k1"} }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k1.k2"}, new String[]{"k1.k2"} }, + + //arrays - key inside array treated as child + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0", new String[]{"k0"} }, + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1", new String[]{"k0.k1"} }, + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1.k2", new String[]{"k0.k1.k2"} },//45 + {"{\"k0\":{\"k1\":[{\"k2\":\"v2\"},{\"k2\":\"v2\"}]}}", "k0.k1.k2", new String[]{"k0.k1.k2", "k0.k1.k2"} }, + }); + } + + @Test + public void test() throws ParseException + { + JSONObject objectToSearch = jsonToSearch != null ? (JSONObject) JSONValue.parseWithException(jsonToSearch) : null; + PathLocator locator = switchKeyToRemove(); + List found = locator.locate(objectToSearch); + assertEquals(Arrays.asList(expectedFound), found); + } + + private PathLocator switchKeyToRemove() + { + long m = System.currentTimeMillis(); + if (keysToFind == null && m % 4 == 0) { + System.out.println("cast to String"); + return new PathLocator((String)null); + } else if (keysToFind == null && m % 4 == 1) { + System.out.println("cast to String[]"); + return new PathLocator((String[])null); + } else if (keysToFind == null && m % 4 == 2) { + System.out.println("cast to JSONArray"); + return new PathLocator((JSONArray)null); + } else if (keysToFind == null && m % 4 == 3) { + System.out.println("cast to List"); + return new PathLocator((List)null); + } else if (keysToFind instanceof String) { + return new PathLocator((String) keysToFind); + } else if (keysToFind instanceof String[]) { + return new PathLocator((String[]) keysToFind); + } else if (keysToFind instanceof JSONArray) { + return new PathLocator((JSONArray) keysToFind); + } else if (keysToFind instanceof List) { + return new PathLocator((List) keysToFind); + } else { + throw new IllegalArgumentException("bad test setup: wrong type of key to remove"); + } + } +} \ No newline at end of file diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/PathRemoverTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathRemoverTest.java new file mode 100644 index 00000000..b50e9d97 --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathRemoverTest.java @@ -0,0 +1,117 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.actions.PathRemover; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import net.minidev.json.parser.ParseException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link PathRemover} + * + * @author adoneitan@gmail.com + */ +@RunWith(Parameterized.class) +public class PathRemoverTest +{ + private String jsonToClean; + private Object keyToRemove; + private String expectedJson; + + public PathRemoverTest(String jsonToClean, Object keyToRemove, String expectedJson) + { + this.jsonToClean = jsonToClean; + this.keyToRemove = keyToRemove; + this.expectedJson = expectedJson; + } + + @Parameterized.Parameters + public static Collection params() { + return Arrays.asList(new Object[][]{ + {null, "key", null }, // null json + {"{}", "key", "{}" }, // empty json + {"{\"first\": null}", null, "{\"first\": null}" }, // null key + {"{\"first\": null}", "", "{\"first\": null}" }, // empty string key + {"{\"first\": null}", new String[]{}, "{\"first\": null}" }, // empty string array key + {"{\"first\": null}", new JSONArray(), "{\"first\": null}" }, // empty json array key + {"{\"first\": null}", new ArrayList(0), "{\"first\": null}" }, // empty list key + {"{\"first\": null}", "first", "{}" }, // remove root key + {"{\"first.f1\": null}", "first.f1", "{}" }, // key with dot + {"{\"first.f1\": \"withDot\", \"first\":{\"f1\": null}}", "first.f1", "{\"first\":{}}" }, //9 key with dot ambiguity + {"{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\"}}}}", "first.f2.f3.id", "{\"first\":{\"f2\":{\"f3\":{}}}}" }, // nested object remove single leaf + {"{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\"}}}}", "notfound", "{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\"}}}}" }, // nested object key does not exist + {"{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\",\"name\":\"me\"}}}}", "first.f2.f3.id", "{\"first\":{\"f2\":{\"f3\":{\"name\":\"me\"}}}}"}, // nested object remove first leaf + {"{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\",\"name\":\"me\"}}}}", "first.f2.f3.name", "{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\"}}}}" }, //13 nested object remove last leaf + {"{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\",\"name\":\"me\"}}}}", "first.f2.f3", "{\"first\":{\"f2\":{}}}" }, // nested object remove intermediate node + {"{\"first\":{\"f2\":{\"f3\":{\"id\":\"id1\",\"name\":\"me\"}}}}", "first", "{}" }, // nested object remove root + {"{\"first\":{\"f2\":[[1,{\"id\":\"id1\"},3],4]}}", "first.f2.id", "{\"first\":{\"f2\":[[1,{},3],4]}}" }, // double nested array remove leaf + {"{\"first\":{\"f2\":[[1,{\"id\":\"id1\"},3],4]}}", "first.f2", "{\"first\":{}}" }, // double nested array remove array + {"{\"first\":[[1,{\"id\":\"id1\"},3],4]}", "first", "{}" }, // double nested array remove root + + //arrays + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1", "{\"k0\":{}}" }, // value is array + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1.k2", "{\"k0\":{\"k1\":[1,{},3,4]}}" }, // full path into array object + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1.3" , "{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}" }, // full path into array primitive + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},{\"k2\":\"v2\"},3,4]}}", "k0.k1.k2", "{\"k0\":{\"k1\":[1,{},{},3,4]}}" }, // full path into array with identical items + + // composite json remove all roots + {"{\"first\": {\"f2\":{\"id\":\"id1\"}}, \"second\": [{\"k1\":{\"id\":\"id1\"}}, 4, 5, 6, {\"id\": 123}], \"third\": 789, \"id\": null}", + (JSONArray) JSONValue.parse("[\"first\",\"second\",\"third\",\"id\"]"), + "{}" }, + // composite json remove all leaves + {"{\"first\": {\"f2\":{\"id\":\"id1\"}}, \"second\": [{\"k1\":{\"id\":\"id1\"}}, 4, 5, 6, {\"id\": 123}], \"third\": 789, \"id\": null}", + (List) Arrays.asList("first.f2.id", "second.k1.id", "second.id", "third", "id"), + "{\"first\": {\"f2\":{}}, \"second\": [{\"k1\":{}}, 4, 5, 6, {}]}" }, + + }); + } + + @Test + public void test() throws ParseException + { + JSONObject objectToClean = jsonToClean != null ? (JSONObject) JSONValue.parseWithException(jsonToClean) : null; + JSONObject expectedObject = expectedJson != null ? (JSONObject) JSONValue.parseWithException(expectedJson): null; + PathRemover cl = switchKeyToRemove(); + cl.remove(objectToClean); + assertEquals(expectedObject, objectToClean); + } + + private PathRemover switchKeyToRemove() + { + long m = System.currentTimeMillis(); + if (keyToRemove == null && m % 4 == 0) { + System.out.println("cast to String"); + return new PathRemover((String)null); + } else if (keyToRemove == null && m % 4 == 1) { + System.out.println("cast to String[]"); + return new PathRemover((String[])null); + } else if (keyToRemove == null && m % 4 == 2) { + System.out.println("cast to JSONArray"); + return new PathRemover((JSONArray)null); + } else if (keyToRemove == null && m % 4 == 3) { + System.out.println("cast to List"); + return new PathRemover((List)null); + } else if (keyToRemove instanceof String) { + return new PathRemover((String)keyToRemove); + } else if (keyToRemove instanceof String[]) { + return new PathRemover((String[])keyToRemove); + } else if (keyToRemove instanceof JSONArray) { + return new PathRemover((JSONArray)keyToRemove); + } else if (keyToRemove instanceof List) { + return new PathRemover((List)keyToRemove); + } else { + throw new IllegalArgumentException("bad test setup: wrong type of key to remove"); + } + } + +} diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/PathReplicatorTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathReplicatorTest.java new file mode 100644 index 00000000..8bcb8db8 --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathReplicatorTest.java @@ -0,0 +1,199 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.actions.PathReplicator; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author adoneitan@gmail.com + */ +@RunWith(Parameterized.class) +public class PathReplicatorTest { + private String jsonSource; + private Object pathsToCopy; + private Object expected; + + public PathReplicatorTest(String jsonSource, Object pathsToCopy, Object expected) { + this.jsonSource = filter(jsonSource); + this.pathsToCopy = filter(pathsToCopy); + this.expected = filter(expected); + } + + @SuppressWarnings("unchecked") + private static T filter(T obj) { + if (obj == null) + return null; + if (obj instanceof String) + return (T)(((String)obj).replace("'", "\"")); + return obj; + } + + @Parameterized.Parameters + public static Collection params() { + return Arrays.asList(new Object[][] { + //nulls, bad/empty keys + {null, null, null }, + {null, "", null }, + {null, "k1", null }, + {null, new String[]{}, null }, + {null, new JSONArray(), null }, + {null, new ArrayList(0), null },//5 + + //empty json, bad/empty keys + {"{}", null, "{}" }, + {"{}", "", "{}" }, + {"{}", "k1", "{}" }, + {"{}", new String[]{}, "{}" }, + {"{}", new JSONArray(), "{}" }, + {"{}", new ArrayList(0), "{}" },//11 + + //simple json, bad/empty keys + {"{'k0':'v0'}", null, "{}" }, + {"{'k0':'v0'}", "", "{}" }, + {"{'k0':'v0'}", "k1", "{}" }, + {"{'k0':'v0'}", new String[]{}, "{}" }, + {"{'k0':'v0'}", new JSONArray(), "{}" }, + {"{'k0':'v0'}", new ArrayList(0), "{}" },//17 + + //simple json, valid/invalid keys + {"{'k0':'v0'}", "k0", "{'k0':'v0'}" }, + {"{'k0':'v0'}", "v0", "{}" }, + {"{'k0':'v0'}", "k0.k1", "{}" },//20 + {"{'k0':'v0'}", "k1.k0", "{}" }, + {"{'k0':null}", "k0", "{'k0':null}" }, + {"{'k0':null}", "v0", "{}" }, + + //key with dot char + {"{'k0.k1':'v0'}", "k0", "{}" }, + {"{'k0.k1':'v0'}", "k1", "{}" }, + {"{'k0.k1':'v0'}", "k0.k1", "{}" }, + + // key with dot ambiguity + {"{'k0.k1':'withDot','k0':{'k1':null}}", "k0", "{'k0':{}}" }, + {"{'k0.k1':'withDot','k0':{'k1':null}}", "k1", "{}" }, + {"{'k0.k1':'withDot','k0':{'k1':null}}", "k0.k1", "{'k0':{'k1':null}}" }, + {"{'k0':{'k1.k2':'dot','k1':{'k2':null}}}", "k0.k1", "{'k0':{'k1':{}}}" }, + {"{'k0':{'k1.k2':'dot','k1':{'k2':null}}}", "k0.k1.k2", "{'k0':{'k1':{'k2':null}}}" }, + {"{'k0':{'k1.k2':'dot','k1':{'k2':null}}}", "k1.k2", "{}" }, + {"{'k0':{'k1.k2':'dot'},'k1':{'k2':'v2'}}}", "k0", "{'k0':{}}}" }, + {"{'k0':{'k1.k2':'dot'},'k1':{'k2':'v2'}}}", "k1.k2", "{'k1':{'k2':'v2'}}}" }, + {"{'k0':{'k1':'v1','k2':{'k3.k4':'dot'}}}", "k0.k2.k3.k4", "{}" }, + {"{'k0':{'k1':'v1','k2':{'k3.k4':'dot'}}}", "k0.k2.k3", "{}" }, + {"{'k0':{'k1':'v1','k2':{'k3.k4':'dot'}}}", "k0.k2", "{'k0':{'k2':{}}}" }, + {"{'k0':{'k1':'v1','k2':{'k3.k4':'dot'}}}", "k0", "{'k0':{}}" },//38 + + //ignore non-existent keys but keep good keys + {"{'k0':'v0','k1':'v1'}", new String[]{"k0","k2"}, "{'k0':'v0'}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0","k2"}, "{'k0':'v0'}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0","k1.k2"}, "{'k0':'v0','k1':{'k2':'v2'}}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0","k0.k2"}, "{'k0':'v0'}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0","k1.k2","k1"}, "{'k0':'v0','k1':{'k2':'v2'}}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0","k1"}, "{'k0':'v0','k1':{}}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0","k1","k2"}, "{'k0':'v0','k1':{}}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k1.k2"}, "{'k1':{'k2':'v2'}}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k1.k2.k3"}, "{}" }, + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k0.k1.k2"}, "{}" },//48 + {"{'k0':'v0','k1':{'k2':'v2'}}", new String[]{"k1.k0"}, "{}" }, + + //arrays - key inside array treated as child + {"{'k0':{'k1':[1,2,3,4]}}", "k0", "{'k0':{}}" }, + {"{'k0':{'k1':[1,2,3,4]}}", "k0.k1", "{'k0':{'k1':[1,2,3,4]}}" }, + {"{'k0':{'k1':[1,{'k2':'v2'},3,4]}}", "k0", "{'k0':{}}" }, + {"{'k0':{'k1':[1,{'k2':'v2'},3,4]}}", "k0.k1", "{'k0':{'k1':[1,{},3,4]}}" }, + {"{'k0':{'k1':[1,{'k2':'v2'},3,4]}}", "k0.k1.k2", "{'k0':{'k1':[{'k2':'v2'}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'},{'k2':'v2'}]}}", "k0.k1", "{'k0':{'k1':[{},{}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'},{'k2':'v2'}]}}", "k0.k1.k2", "{'k0':{'k1':[{'k2':'v2'},{'k2':'v2'}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':'v4'}]}}", "k0.k1.k2", "{'k0':{'k1':[{'k2':'v2'}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':'v4'}]}}", "k0.k3.k4", "{'k0':{'k3':[{'k4':'v4'}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':{'k5':'v5'}}]}}", "k0.k1.k2", "{'k0':{'k1':[{'k2':'v2'}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':{'k5':'v5'}}]}}", "k0.k3.k4", "{'k0':{'k3':[{'k4':{}}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':{'k5':'v5'}}]}}", "k0.k3.k4.k5", "{'k0':{'k3':[{'k4':{'k5':'v5'}}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':{'k5':'v5'}}]}}", new String[]{"k0.k1", "k0.k3"}, "{'k0':{'k3':[{}],'k1':[{}]}}" }, + {"{'k0':{'k1':[{'k2':'v2'}],'k3':[{'k4':{'k5':'v5'}}]}}", new String[]{"k0.k1", "k0.k3.k4.k5"}, "{'k0':{'k3':[{'k4':{'k5':'v5'}}],'k1':[{}]}}" }, + }); + } + + @Test + public void test() throws Exception { + JSONObject objectSource = jsonSource != null ? (JSONObject) JSONValue.parseWithException(jsonSource) : null; + PathReplicator copier = switchKeyToCopy(); + JSONObject copied = copier.replicate(objectSource); + JSONObject expectedObj = expected != null ? (JSONObject) JSONValue.parseWithException((String) expected) : null; + assertEquals(expectedObj, copied); + } + + @Test + public void test2() throws Exception { + JSONObject objectSource = jsonSource != null ? (JSONObject) JSONValue.parseWithException(jsonSource) : null; + PathReplicator copier = switchKeyToCopy2(); + JSONObject copied = copier.replicate(objectSource); + JSONObject expectedObj = expected != null ? (JSONObject) JSONValue.parseWithException((String) expected) : null; + assertEquals(expectedObj, copied); + } + + private PathReplicator switchKeyToCopy() { + long m = System.currentTimeMillis(); + if (pathsToCopy == null && m % 4 == 0) { + System.out.println("cast to String"); + return new PathReplicator((String) null); + } else if (pathsToCopy == null && m % 4 == 1) { + System.out.println("cast to String[]"); + return new PathReplicator((String[]) null); + } else if (pathsToCopy == null && m % 4 == 2) { + System.out.println("cast to JSONArray"); + return new PathReplicator((JSONArray) null); + } else if (pathsToCopy == null && m % 4 == 3) { + System.out.println("cast to List"); + return new PathReplicator((List) null); + } else if (pathsToCopy instanceof String) { + return new PathReplicator((String) pathsToCopy); + } else if (pathsToCopy instanceof String[]) { + return new PathReplicator((String[]) pathsToCopy); + } else if (pathsToCopy instanceof JSONArray) { + return new PathReplicator((JSONArray) pathsToCopy); + } else if (pathsToCopy instanceof List) { + return new PathReplicator((List) pathsToCopy); + } else { + throw new IllegalArgumentException("bad test setup: wrong type of key to remove"); + } + } + + private PathReplicator switchKeyToCopy2() { + long m = System.currentTimeMillis(); + if (pathsToCopy == null && m % 4 == 0) { + System.out.println("cast to String"); + return new PathReplicator((String) null); + } else if (pathsToCopy == null && m % 4 == 1) { + System.out.println("cast to String[]"); + return new PathReplicator((String[]) null); + } else if (pathsToCopy == null && m % 4 == 2) { + System.out.println("cast to JSONArray"); + return new PathReplicator((JSONArray) null); + } else if (pathsToCopy == null && m % 4 == 3) { + System.out.println("cast to List"); + return new PathReplicator((List) null); + } else if (pathsToCopy instanceof String) { + return new PathReplicator((String) pathsToCopy); + } else if (pathsToCopy instanceof String[]) { + return new PathReplicator((String[]) pathsToCopy); + } else if (pathsToCopy instanceof JSONArray) { + return new PathReplicator((JSONArray) pathsToCopy); + } else if (pathsToCopy instanceof List) { + return new PathReplicator((List) pathsToCopy); + } else { + throw new IllegalArgumentException("bad test setup: wrong type of key to remove"); + } + } + +} \ No newline at end of file diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/PathsRetainerTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathsRetainerTest.java new file mode 100644 index 00000000..f079e801 --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/PathsRetainerTest.java @@ -0,0 +1,148 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.actions.PathsRetainer; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import net.minidev.json.actions.path.DotDelimiter; +import net.minidev.json.parser.ParseException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author adoneitan@gmail.com + */ +@RunWith(Parameterized.class) +public class PathsRetainerTest { + private String jsonToReduce; + private Object keyToKeep; + private String expectedReducedJson; + + public PathsRetainerTest(String jsonToReduce, Object keyToKeep, String expectedReducedJson) { + this.jsonToReduce = jsonToReduce; + this.keyToKeep = keyToKeep; + this.expectedReducedJson = expectedReducedJson; + } + + @Parameterized.Parameters + public static Collection params() { + return Arrays + .asList(new Object[][] { + + //nulls, bad/empty keys + {null, null, null }, + {null, "", null }, + {null, "k1", null }, + {null, new String[]{}, null }, + {null, new JSONArray(), null }, + {null, new ArrayList(0), null },//5 + + //empty json, bad/empty keys + {"{}", null, "{}" }, + {"{}", "", "{}" }, + {"{}", "k1", "{}" }, + {"{}", new String[]{}, "{}" }, + {"{}", new JSONArray(), "{}" }, + {"{}", new ArrayList(0), "{}" },//11 + + //simple json, bad/empty keys + {"{\"k0\":\"v0\"}", null, "{}" }, + {"{\"k0\":\"v0\"}", "", "{}" }, + {"{\"k0\":\"v0\"}", "k1", "{}" }, + {"{\"k0\":\"v0\"}", new String[]{}, "{}" }, + {"{\"k0\":\"v0\"}", new JSONArray(), "{}" }, + {"{\"k0\":\"v0\"}", new ArrayList(0), "{}" },//17 + + //simple json, valid/invalid keys + {"{\"k0\":\"v0\"}", "k0", "{\"k0\":\"v0\"}" }, + {"{\"k0\":\"v0\"}", "v0", "{}" }, + {"{\"k0\":\"v0\"}", "k0.k1", "{}" }, + {"{\"k0\":\"v0\"}", "k1.k0", "{}" }, + {"{\"k0\":null}", "k0", "{\"k0\":null}" }, + {"{\"k0\":null}", "v0", "{}" },//23 + + //key with dot char + {"{\"k0.k1\":\"v0\"}", "k0", "{}" }, + {"{\"k0.k1\":\"v0\"}", "k1", "{}" }, + {"{\"k0.k1\":\"v0\"}", "k0.k1", "{}" }, + + // key with dot ambiguity + {"{\"k0.k1\":\"withDot\",\"k0\":{\"k1\":null}}", "k0", "{\"k0\":{}}" },//27 + {"{\"k0.k1\":\"withDot\",\"k0\":{\"k1\":null}}", "k1", "{}" }, + {"{\"k0.k1\":\"withDot\",\"k0\":{\"k1\":null}}", "k0.k1", "{\"k0\":{\"k1\":null}}" },//29 + {"{\"k0\":{\"k1.k2\":\"dot\",\"k1\":{\"k2\":null}}}", "k0.k1", "{\"k0\":{\"k1\":{}}}" }, + {"{\"k0\":{\"k1.k2\":\"dot\",\"k1\":{\"k2\":null}}}", "k0.k1.k2", "{\"k0\":{\"k1\":{\"k2\":null}}}" },//31 + {"{\"k0\":{\"k1.k2\":\"dot\",\"k1\":{\"k2\":null}}}", "k1.k2", "{}" }, + {"{\"k0\":{\"k1.k2\":\"dot\"},\"k1\":{\"k2\":\"v2\"}}}","k0", "{\"k0\":{}}}" }, + {"{\"k0\":{\"k1.k2\":\"dot\"},\"k1\":{\"k2\":\"v2\"}}}","k1.k2", "{\"k1\":{\"k2\":\"v2\"}}}" }, + {"{\"k0\":{\"k1\":\"v1\",\"k2\":{\"k3.k4\":\"dot\"}}}", "k0.k2.k3.k4", "{}" }, + {"{\"k0\":{\"k1\":\"v1\",\"k2\":{\"k3.k4\":\"dot\"}}}", "k0.k2.k3", "{}" }, + {"{\"k0\":{\"k1\":\"v1\",\"k2\":{\"k3.k4\":\"dot\"}}}", "k0.k2", "{\"k0\":{\"k2\":{}}}" }, + {"{\"k0\":{\"k1\":\"v1\",\"k2\":{\"k3.k4\":\"dot\"}}}", "k0", "{\"k0\":{}}" }, + + //ignore non-existent keys but keep good keys + {"{\"k0\":\"v0\",\"k1\":\"v1\"}", new String[]{"k0","k2"}, "{\"k0\":\"v0\"}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k2"}, "{\"k0\":\"v0\"}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1.k2"}, "{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k0.k2"}, "{\"k0\":\"v0\"}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1.k2","k1"}, "{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1"}, "{\"k0\":\"v0\",\"k1\":{}}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0","k1","k2"}, "{\"k0\":\"v0\",\"k1\":{}}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k1.k2"}, "{\"k1\":{\"k2\":\"v2\"}}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k1.k2.k3"}, "{}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k0.k1.k2"}, "{}" }, + {"{\"k0\":\"v0\",\"k1\":{\"k2\":\"v2\"}}", new String[]{"k1.k0"}, "{}" }, + + //arrays - key inside array treated as child + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0", "{\"k0\":{}}" }, + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1", "{\"k0\":{\"k1\":[1,{},3,4]}}" }, + {"{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}", "k0.k1.k2", "{\"k0\":{\"k1\":[1,{\"k2\":\"v2\"},3,4]}}" }, + {"{\"k0\":{\"k1\":[{\"k2\":\"v2\"},{\"k2\":\"v2\"}]}}", "k0.k1", "{\"k0\":{\"k1\":[{},{}]}}" }, + {"{\"k0\":{\"k1\":[{\"k2\":\"v2\"},{\"k2\":\"v2\"}]}}", "k0.k1.k2", "{\"k0\":{\"k1\":[{\"k2\":\"v2\"},{\"k2\":\"v2\"}]}}" }, + }); + } + + @Test + public void test() throws ParseException { + JSONObject objectToReduce = jsonToReduce != null ? (JSONObject) JSONValue.parseWithException(jsonToReduce) : null; + JSONObject expectedReducedObj = expectedReducedJson != null ? (JSONObject) JSONValue.parseWithException(expectedReducedJson) : null; + PathsRetainer retainer = switchKeyToRemove().with(new DotDelimiter().withAcceptDelimiterInNodeName(false)); + JSONObject reducedObj = retainer.retain(objectToReduce); + assertEquals(expectedReducedObj, reducedObj); + } + + private PathsRetainer switchKeyToRemove() { + long m = System.currentTimeMillis(); + if (keyToKeep == null && m % 4 == 0) { + System.out.println("cast to String"); + return new PathsRetainer((String) null); + } else if (keyToKeep == null && m % 4 == 1) { + System.out.println("cast to String[]"); + return new PathsRetainer((String[]) null); + } else if (keyToKeep == null && m % 4 == 2) { + System.out.println("cast to JSONArray"); + return new PathsRetainer((JSONArray) null); + } else if (keyToKeep == null && m % 4 == 3) { + System.out.println("cast to List"); + return new PathsRetainer((List) null); + } else if (keyToKeep instanceof String) { + return new PathsRetainer((String) keyToKeep); + } else if (keyToKeep instanceof String[]) { + return new PathsRetainer((String[]) keyToKeep); + } else if (keyToKeep instanceof JSONArray) { + return new PathsRetainer((JSONArray) keyToKeep); + } else if (keyToKeep instanceof List) { + return new PathsRetainer((List) keyToKeep); + } else { + throw new IllegalArgumentException("bad test setup: wrong type of key to remove"); + } + } +} \ No newline at end of file diff --git a/json-smart-action/src/test/java/net/minidev/json/test/actions/TreePathTest.java b/json-smart-action/src/test/java/net/minidev/json/test/actions/TreePathTest.java new file mode 100644 index 00000000..4a890f39 --- /dev/null +++ b/json-smart-action/src/test/java/net/minidev/json/test/actions/TreePathTest.java @@ -0,0 +1,93 @@ +package net.minidev.json.test.actions; + +import net.minidev.json.actions.path.DotDelimiter; +import net.minidev.json.actions.path.TreePath; +import net.minidev.json.actions.path.PathDelimiter; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author adoneitan@gmail.com + */ +public class TreePathTest { + private static final PathDelimiter delim = new DotDelimiter().withAcceptDelimiterInNodeName(true); + + @Test + public void testIterator() { + TreePath jp = new TreePath("a.b.c", delim); + assertTrue(jp.nextIndex() == 0); + assertTrue(jp.prevIndex() == -1); + assertTrue("".equals(jp.curr())); + assertTrue("".equals(jp.origin())); + assertTrue("a.b.c".equals(jp.remainder())); + assertTrue(jp.hasNext()); + assertFalse(jp.hasPrev()); + + jp.next(); + assertTrue("a".equals(jp.curr())); + assertTrue("a".equals(jp.origin())); + assertTrue("b.c".equals(jp.remainder())); + assertTrue(jp.hasNext()); + assertTrue(jp.hasPrev()); + + jp.next(); + assertTrue("b".equals(jp.curr())); + assertTrue("a.b".equals(jp.origin())); + assertTrue("c".equals(jp.remainder())); + assertTrue(jp.hasNext()); + assertTrue(jp.hasPrev()); + + jp.next(); + assertTrue("c".equals(jp.curr())); + assertTrue("a.b.c".equals(jp.origin())); + assertTrue("".equals(jp.remainder())); + assertFalse(jp.hasNext()); + assertTrue(jp.hasPrev()); + + /** the first prev() after a next only changes direction. see {@link ListIterator} for details */ + jp.prev(); + assertTrue("c".equals(jp.curr())); + assertTrue("a.b.c".equals(jp.origin())); + assertTrue("".equals(jp.remainder())); + assertTrue(jp.hasNext()); + assertTrue(jp.hasPrev()); + + jp.prev(); + assertTrue("b".equals(jp.curr())); + assertTrue("a.b".equals(jp.origin())); + assertTrue("c".equals(jp.remainder())); + assertTrue(jp.hasNext()); + assertTrue(jp.hasPrev()); + + jp.prev(); + assertTrue("a".equals(jp.curr())); + assertTrue("a".equals(jp.origin())); + assertTrue("b.c".equals(jp.remainder())); + assertTrue(jp.hasNext()); + assertFalse(jp.hasPrev()); + } + + @Test + public void testSubPath() { + TreePath jp = new TreePath("a.b.c", delim); + assertTrue(jp.subPath(1, 2).equals("b.c")); + } + + @Test + public void testClone() throws CloneNotSupportedException { + TreePath jp1 = new TreePath("a.b.c", delim); + TreePath jp2 = jp1.clone(); + assertTrue(jp1.equals(jp2)); + + jp1.next(); + TreePath jp3 = jp1.clone(); + assertTrue(jp1.equals(jp3)); + + jp1.prev(); + TreePath jp4 = jp1.clone(); + assertTrue(jp1.equals(jp4)); + + } +} \ No newline at end of file diff --git a/json-smart/pom.xml b/json-smart/pom.xml index a7d9430b..f3f47102 100644 --- a/json-smart/pom.xml +++ b/json-smart/pom.xml @@ -1,106 +1,99 @@ - - - 4.0.0 - net.minidev - json-smart - 2.2.2 - JSON Small and Fast Parser - + + + net.minidev + minidev-parent + 2.3-SNAPSHOT + + + 4.0.0 + json-smart + JSON Small and Fast Parser + JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language. - bundle - http://www.minidev.net/ - - Chemouni Uriel - http://www.minidev.net/ - - - - uriel - Uriel Chemouni - uchemouni@gmail.com - GMT+1 - - - - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - All files under Apache 2 - - - - UTF-8 - 1.5 - 1.5 - - - - junit - junit - 4.12 - test - - - net.minidev - accessors-smart - 1.2 - - - - - - - - - - - scm:git:https://github.com/netplex/json-smart-v2.git - scm:git:https://github.com/netplex/json-smart-v2.git - https://github.com/netplex/json-smart-v2 - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - release-sign-artifacts - - - - performRelease - true - - - - - 2C8DF6EC - - - - - - - + scm:git:https://github.com/netplex/json-smart-v2.git + scm:git:https://github.com/netplex/json-smart-v2.git + https://github.com/netplex/json-smart-v2 + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + release-sign-artifacts + + + + performRelease + true + + + + + 2C8DF6EC + + + + + + + - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - - jar - - - - - + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + - - org.apache.maven.plugins - maven-release-plugin - 2.5.2 - - forked-path - -Psonatype-oss-release - false - false - release - deploy - - - - - - - include-sources - - - - / - true - src/main/java - - **/*.java - - - - - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.4 - - - bind-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.3 - - UTF-8 - 1.6 - 1.6 - - **/.svn/* - **/.svn - - - - - - org.apache.maven.plugins - maven-resources-plugin - 2.7 - - UTF-8 - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - **/.svn/* - **/.svn - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - - false - - - - - attach-javadocs - - jar - - - - - - org.apache.felix - maven-bundle-plugin - 3.0.0 - true - - - ${project.groupId}.${project.artifactId} - ${project.artifactId} - ${project.version} - - net.minidev.json, net.minidev.json.annotate, net.minidev.json.parser, net.minidev.json.reader, net.minidev.json.writer - - - - - - - + + org.apache.maven.plugins + maven-release-plugin + 2.5.2 + + forked-path + -Psonatype-oss-release + false + false + release + deploy + + + + + + + include-sources + + + + / + true + src/main/java + + **/*.java + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + bind-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + UTF-8 + 1.6 + 1.6 + + **/.svn/* + **/.svn + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.7 + + UTF-8 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + **/.svn/* + **/.svn + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.3 + + + false + + + + + attach-javadocs + + jar + + + + + + org.apache.felix + maven-bundle-plugin + 3.0.0 + true + + + ${project.groupId}.${project.artifactId} + ${project.artifactId} + ${project.version} + + net.minidev.json, net.minidev.json.annotate, + net.minidev.json.parser, + net.minidev.json.reader, + net.minidev.json.writer + + + + + + + \ No newline at end of file diff --git a/json-smart/src/main/java/net/minidev/json/JSONArray.java b/json-smart/src/main/java/net/minidev/json/JSONArray.java index 18037687..9e6edbe9 100644 --- a/json-smart/src/main/java/net/minidev/json/JSONArray.java +++ b/json-smart/src/main/java/net/minidev/json/JSONArray.java @@ -79,6 +79,18 @@ public static void writeJSONString(List list, Appendable out) writeJSONString(list, out, JSONValue.COMPRESSION); } + /** + * Appends the specified element and returns this. + * Handy alternative to add(E e) method. + * + * @param element element to be appended to this array. + * @return this + */ + public JSONArray appendElement(Object element) { + add(element); + return this; + } + public void merge(Object o2) { JSONObject.merge(this, o2); } diff --git a/json-smart/src/main/java/net/minidev/json/JSONObject.java b/json-smart/src/main/java/net/minidev/json/JSONObject.java index 312e123a..72182177 100644 --- a/json-smart/src/main/java/net/minidev/json/JSONObject.java +++ b/json-smart/src/main/java/net/minidev/json/JSONObject.java @@ -98,6 +98,19 @@ else if (!compression.mustProtectKey(key)) JSONValue.writeJSONString(value, out, compression); } + /** + * Puts value to object and returns this. + * Handy alternative to put(String key, Object value) method. + * + * @param fieldName key with which the specified value is to be associated + * @param fieldValue value to be associated with the specified key + * @return this + */ + public JSONObject appendField(String fieldName, Object fieldValue) { + put(fieldName, fieldValue); + return this; + } + /** * A Simple Helper object to String * diff --git a/json-smart/src/main/java/net/minidev/json/JSONStyle.java b/json-smart/src/main/java/net/minidev/json/JSONStyle.java index 9350d183..a024c6d5 100644 --- a/json-smart/src/main/java/net/minidev/json/JSONStyle.java +++ b/json-smart/src/main/java/net/minidev/json/JSONStyle.java @@ -106,7 +106,7 @@ public boolean protectValues() { public boolean protect4Web() { return _protect4Web; } - + public boolean ignoreNull() { return _ignore_null; } @@ -122,7 +122,7 @@ public boolean mustProtectKey(String s) { public boolean mustProtectValue(String s) { return mpValue.mustBeProtect(s); } - + public void writeString(Appendable out, String value) throws IOException { if (!this.mustProtectValue(value)) out.append(value); @@ -136,7 +136,7 @@ public void writeString(Appendable out, String value) throws IOException { public void escape(String s, Appendable out) { esc.escape(s, out); } - + /** * begin Object */ diff --git a/json-smart/src/main/java/net/minidev/json/JSONValue.java b/json-smart/src/main/java/net/minidev/json/JSONValue.java index c26ef32c..97b8aea3 100644 --- a/json-smart/src/main/java/net/minidev/json/JSONValue.java +++ b/json-smart/src/main/java/net/minidev/json/JSONValue.java @@ -102,7 +102,6 @@ public static T parse(InputStream in, Class mapTo) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, defaultReader.getMapper(mapTo)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -139,7 +138,6 @@ public static T parse(byte[] in, Class mapTo) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, defaultReader.getMapper(mapTo)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -156,7 +154,6 @@ public static T parse(Reader in, Class mapTo) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, defaultReader.getMapper(mapTo)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -173,7 +170,6 @@ public static T parse(Reader in, T toUpdate) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, new UpdaterMapper(defaultReader, toUpdate)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -188,7 +184,6 @@ protected static T parse(Reader in, JsonReaderI mapper) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, mapper); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -205,7 +200,6 @@ public static T parse(String in, Class mapTo) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, defaultReader.getMapper(mapTo)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -222,7 +216,6 @@ public static T parse(InputStream in, T toUpdate) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, new UpdaterMapper(defaultReader, toUpdate)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -239,7 +232,6 @@ public static T parse(String in, T toUpdate) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, new UpdaterMapper(defaultReader, toUpdate)); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -268,7 +260,6 @@ protected static T parse(String in, JsonReaderI mapper) { JSONParser p = new JSONParser(DEFAULT_PERMISSIVE_MODE); return p.parse(in, mapper); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -352,7 +343,6 @@ public static String compress(String input, JSONStyle style) { new JSONParser(DEFAULT_PERMISSIVE_MODE).parse(input, new CompessorMapper(defaultReader, sb, style)); return sb.toString(); } catch (Exception e) { - e.printStackTrace(); return input; } } diff --git a/json-smart/src/main/java/net/minidev/json/annotate/JsonIgnore.java b/json-smart/src/main/java/net/minidev/json/annotate/JsonIgnore.java index a45e4465..77ad017f 100644 --- a/json-smart/src/main/java/net/minidev/json/annotate/JsonIgnore.java +++ b/json-smart/src/main/java/net/minidev/json/annotate/JsonIgnore.java @@ -20,8 +20,6 @@ * Read/Write using field if the field is public (default ) * * - * - * * @author uriel * */ diff --git a/json-smart/src/test/java/net/minidev/json/test/TestNavi.java b/json-smart/src/test/java/net/minidev/json/test/TestNavi.java index 53ebddb5..f4d22f82 100644 --- a/json-smart/src/test/java/net/minidev/json/test/TestNavi.java +++ b/json-smart/src/test/java/net/minidev/json/test/TestNavi.java @@ -49,6 +49,6 @@ public void testNaviWriteArray() { nav = JSONNavi.newInstance(); nav.set("type", "bundle").at("data").array().atNext().set("type", "object").set("name", "obj1").up().atNext().set("type", "object").set("name", "obj2").root(); s2 = nav.toString(); - assertEquals(expected, nav.toString()); + assertEquals(expected, s2); } } diff --git a/json-smart/src/test/java/net/minidev/json/test/strict/TestExcessiveComma.java b/json-smart/src/test/java/net/minidev/json/test/strict/TestExcessiveComma.java index c24b3a5d..6d072929 100644 --- a/json-smart/src/test/java/net/minidev/json/test/strict/TestExcessiveComma.java +++ b/json-smart/src/test/java/net/minidev/json/test/strict/TestExcessiveComma.java @@ -23,14 +23,13 @@ public void testExcessiveComma3A() throws Exception { MustThrows.testStrictInvalidJson(s, ParseException.ERROR_UNEXPECTED_CHAR); JSONValue.parseWithException(s); } - + public void testExcessiveComma4A() throws Exception { String s = "[,5]"; MustThrows.testStrictInvalidJson(s, ParseException.ERROR_UNEXPECTED_CHAR); JSONValue.parseWithException(s); } - public void testExcessiveComma1O() throws Exception { String s = "{\"a\":1,,\"b\":1}"; MustThrows.testStrictInvalidJson(s, ParseException.ERROR_UNEXPECTED_CHAR); diff --git a/json-smart/src/test/java/net/minidev/json/test/strict/TestTaillingJunk.java b/json-smart/src/test/java/net/minidev/json/test/strict/TestTaillingJunk.java index 5f25fcec..9b204acd 100644 --- a/json-smart/src/test/java/net/minidev/json/test/strict/TestTaillingJunk.java +++ b/json-smart/src/test/java/net/minidev/json/test/strict/TestTaillingJunk.java @@ -14,7 +14,7 @@ public class TestTaillingJunk extends TestCase { public void testTaillingSpace() throws Exception { String s = "{\"t\":0} "; MustThrows.testInvalidJson(s, JSONParser.MODE_STRICTEST, ParseException.ERROR_UNEXPECTED_TOKEN); - + s = "{\"t\":0} "; JSONObject o = (JSONObject) new JSONParser(JSONParser.MODE_STRICTEST | JSONParser.ACCEPT_TAILLING_SPACE).parse(s); assertEquals(o.get("t"), 0); @@ -36,9 +36,9 @@ public void testTaillingDataPermisive() throws Exception { JSONObject o = (JSONObject) new JSONParser(JSONParser.MODE_PERMISSIVE).parse(s); assertEquals(o.get("t"), 0); } - + public void testTaillingDataWithSpaceAllowed() throws Exception { - String s = "{\"t\":0}{"; - MustThrows.testInvalidJson(s, JSONParser.MODE_STRICTEST | JSONParser.ACCEPT_TAILLING_SPACE, ParseException.ERROR_UNEXPECTED_TOKEN); - } + String s = "{\"t\":0}{"; + MustThrows.testInvalidJson(s, JSONParser.MODE_STRICTEST | JSONParser.ACCEPT_TAILLING_SPACE, ParseException.ERROR_UNEXPECTED_TOKEN); + } } diff --git a/json-smart/src/test/java/net/minidev/json/test/strict/TestZeroLead.java b/json-smart/src/test/java/net/minidev/json/test/strict/TestZeroLead.java index e163f408..0ed53e00 100644 --- a/json-smart/src/test/java/net/minidev/json/test/strict/TestZeroLead.java +++ b/json-smart/src/test/java/net/minidev/json/test/strict/TestZeroLead.java @@ -42,7 +42,7 @@ public void test01Float() throws Exception { // PERMISIVE JSONValue.parseWithException(s); } - + public void test00001() throws Exception { String s = "{\"t\":00001}"; JSONObject o = (JSONObject) new JSONParser(JSONParser.MODE_PERMISSIVE).parse(s); @@ -57,14 +57,14 @@ public void test00001Strict() throws Exception { } // disable in 1.1 -// public void testDup() throws Exception { -// String s = "{'t':1,'t':2}"; -// try { -// new JSONParser(JSONParser.MODE_PERMISSIVE).parse(s); -// assertEquals("Should Stack", ""); -// } catch (ParseException e) { -// assertEquals(ParseException.ERROR_UNEXPECTED_DUPLICATE_KEY, e.getErrorType()); -// } -// } + // public void testDup() throws Exception { + // String s = "{'t':1,'t':2}"; + // try { + // new JSONParser(JSONParser.MODE_PERMISSIVE).parse(s); + // assertEquals("Should Stack", ""); + // } catch (ParseException e) { + // assertEquals(ParseException.ERROR_UNEXPECTED_DUPLICATE_KEY, e.getErrorType()); + // } + // } } diff --git a/json-smart/src/test/java/net/minidev/json/testMapping/TestCustomMappingInstant.java b/json-smart/src/test/java/net/minidev/json/testMapping/TestCustomMappingInstant.java index c06dcec6..49884c64 100644 --- a/json-smart/src/test/java/net/minidev/json/testMapping/TestCustomMappingInstant.java +++ b/json-smart/src/test/java/net/minidev/json/testMapping/TestCustomMappingInstant.java @@ -2,7 +2,6 @@ import java.io.IOException; -import junit.framework.Assert; import junit.framework.TestCase; import net.minidev.json.JSONStyle; import net.minidev.json.JSONValue; @@ -23,7 +22,7 @@ public void test_dummy() throws IOException { ParseException e = null; JSONValue.toJSONString(true, JSONStyle.MAX_COMPRESS); - Assert.assertEquals(true, true); + //Assert.assertEquals(true, true); } // Need JDK 1.8 diff --git a/parent/pom.xml b/parent/pom.xml index 8b66677b..b6f91bea 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.minidev minidev-parent - 2.2.1 + 2.3-SNAPSHOT Minidev super pom minidev common properties. pom @@ -19,7 +19,7 @@ uriel Uriel Chemouni uchemouni@gmail.com - GMT+1 + GMT-7 @@ -140,6 +140,7 @@ ../accessors-smart + ../json-smart-action ../json-smart @@ -158,12 +159,8 @@ ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ - + @@ -232,8 +229,8 @@ maven-release-plugin 2.5.2 - forked-path - -Psonatype-oss-release + forked-path + -Psonatype-oss-release false false release @@ -266,6 +263,11 @@ json-smart ${project.version} + + net.minidev + json-smart-action + ${project.version} + net.minidev accessors-smart @@ -279,7 +281,7 @@ junit junit - 3.8.2 + 4.12