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 extends Object> 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