├── .gitignore ├── BappDescription.html ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── BappManifest.bmf ├── README.md ├── LICENSE.txt ├── gradlew.bat ├── gradlew └── src ├── burp └── BurpExtender.java └── mjson └── Json.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | -------------------------------------------------------------------------------- /BappDescription.html: -------------------------------------------------------------------------------- 1 |
This extension copies selected request(s) as Python-Requests invocations.
2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentsignal/burp-requests/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /BappManifest.bmf: -------------------------------------------------------------------------------- 1 | Uuid: b324647b6efa4b6a8f346389730df160 2 | ExtensionType: 1 3 | Name: Copy As Python-Requests 4 | RepoName: copy-as-python-requests 5 | ScreenVersion: 0.2.6 6 | SerialVersion: 9 7 | MinPlatformVersion: 0 8 | ProOnly: False 9 | Author: Andras Veres-Szentkiralyi 10 | ShortDescription: Copies selected request(s) as Python-Requests invocations. 11 | EntryPoint: build/libs/copy-as-python-requests.jar 12 | BuildCommand: ./gradlew jar 13 | SupportedProducts: Pro, Community 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Copy as requests plugin for Burp Suite 2 | ====================================== 3 | 4 | Copies selected request(s) as Python [requests][1] invocation. 5 | 6 | Building 7 | -------- 8 | 9 | Execute `./gradlew build` and you'll have the plugin ready in `build/libs/burp-requests.jar` 10 | 11 | License 12 | ------- 13 | 14 | The whole project is available under MIT license, see `LICENSE.txt`, 15 | except for the [Mjson library][2], where 16 | 17 | > The source code is a single Java file. [...] Some of it was ripped 18 | > off from other projects and credit and licensing notices are included 19 | > in the appropriate places. The license is Apache 2.0. 20 | 21 | [1]: http://docs.python-requests.org/ 22 | [2]: https://bolerio.github.io/mjson/ 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Andras Veres-Szentkiralyi 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.*; 4 | import java.awt.datatransfer.*; 5 | import java.awt.event.*; 6 | import java.awt.Toolkit; 7 | import java.io.UnsupportedEncodingException; 8 | import javax.swing.JMenuItem; 9 | 10 | import mjson.Json; 11 | 12 | public class BurpExtender implements IBurpExtender, IContextMenuFactory, ClipboardOwner 13 | { 14 | private IExtensionHelpers helpers; 15 | 16 | private final static String NAME = "Copy as requests"; 17 | private final static String SESSION_MENU_ITEM = NAME + " with session object"; 18 | private final static String[] PYTHON_ESCAPE = new String[256]; 19 | private final static String SESSION_VAR = "session"; 20 | 21 | static { 22 | for (int i = 0x00; i <= 0xFF; i++) PYTHON_ESCAPE[i] = String.format("\\x%02x", i); 23 | for (int i = 0x20; i < 0x80; i++) PYTHON_ESCAPE[i] = String.valueOf((char)i); 24 | PYTHON_ESCAPE['\n'] = "\\n"; 25 | PYTHON_ESCAPE['\r'] = "\\r"; 26 | PYTHON_ESCAPE['\t'] = "\\t"; 27 | PYTHON_ESCAPE['"'] = "\\\""; 28 | PYTHON_ESCAPE['\\'] = "\\\\"; 29 | } 30 | 31 | @Override 32 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) 33 | { 34 | helpers = callbacks.getHelpers(); 35 | callbacks.setExtensionName(NAME); 36 | callbacks.registerContextMenuFactory(this); 37 | } 38 | 39 | @Override 40 | public List45 | * Represents a JSON (JavaScript Object Notation) entity. For more information about JSON, please see 46 | * http://www.json.org. 47 | *
48 | * 49 | *
50 | * A JSON entity can be one of several things: an object (set of name/Json entity pairs), an array (a list of
51 | * other JSON entities), a string, a number, a boolean or null. All of those are represented as Json
52 | * instances. Each of the different types of entities supports a different set of operations. However, this class
53 | * unifies all operations into a single interface so in Java one is always dealing with a single object type: this class.
54 | * The approach effectively amounts to dynamic typing where using an unsupported operation won't be detected at
55 | * compile time, but will throw a runtime {@link UnsupportedOperationException}. It simplifies working with JSON
56 | * structures considerably and it leads to shorter at cleaner Java code. It makes much easier to work
57 | * with JSON structure without the need to convert to "proper" Java representation in the form of
58 | * POJOs and the like. When traversing a JSON, there's no need to type-cast at each step because there's
59 | * only one type: Json.
60 | *
63 | * One can examine the concrete type of a Json with one of the isXXX methods:
64 | * {@link #isObject()}, {@link #isArray()},{@link #isNumber()},{@link #isBoolean()},{@link #isString()},
65 | * {@link #isNull()}.
66 | *
69 | * The underlying representation of a given Json instance can be obtained by calling
70 | * the generic {@link #getValue()} method or one of the asXXX methods such
71 | * as {@link #asBoolean()} or {@link #asString()} etc.
72 | * JSON objects are represented as Java {@link Map}s while JSON arrays are represented as Java
73 | * {@link List}s. Because those are mutable aggregate structures, there are two versions of the
74 | * corresponding asXXX methods: {@link #asMap()} which performs a deep copy of the underlying
75 | * map, unwrapping every nested Json entity to its Java representation and {@link #asJsonMap()} which
76 | * simply return the map reference. Similarly there are {@link #asList()} and {@link #asJsonList()}.
77 | *
82 | * There are several static factory methods in this class that allow you to create new
83 | * Json instances:
84 | *
| {@link #read(String)} | 88 | *Parse a JSON string and return the resulting Json instance. The syntax
89 | * recognized is as defined in http://www.json.org.
90 | * |
91 | *
| {@link #make(Object)} | 93 | *Creates a Json instance based on the concrete type of the parameter. The types
94 | * recognized are null, numbers, primitives, String, Map, Collection, Java arrays
95 | * and Json itself. |
96 | *
| {@link #nil()} | 98 | *Return a Json instance representing JSON null. |
99 | *
| {@link #object()} | 101 | *Create and return an empty JSON object. | 102 | *
| {@link #object(Object...)} | 104 | *Create and return a JSON object populated with the key/value pairs
105 | * passed as an argument sequence. Each even parameter becomes a key (via
106 | * toString) and each odd parameter is converted to a Json
107 | * value. |
108 | *
| {@link #array()} | 110 | *Create and return an empty JSON array. | 111 | *
| {@link #array(Object...)} | 113 | *Create and return a JSON array from the list of arguments. | 114 | *
118 | * To customize how Json elements are represented and to provide your own version of the 119 | * {@link #make(Object)} method, you create an implementation of the {@link Factory} interface 120 | * and configure it either globally with the {@link #setGlobalFactory(Factory)} method or 121 | * on a per-thread basis with the {@link #attachFactory(Factory)}/{@link #detachFactory()} 122 | * methods. 123 | *
124 | * 125 | *
126 | * If a Json instance is an object, you can set its properties by
127 | * calling the {@link #set(String, Object)} method which will add a new property or replace an existing one.
128 | * Adding elements to an array Json is done with the {@link #add(Object)} method.
129 | * Removing elements by their index (or key) is done with the {@link #delAt(int)} (or
130 | * {@link #delAt(String)}) method. You can also remove an element from an array without
131 | * knowing its index with the {@link #remove(Object)} method. All these methods return the
132 | * Json instance being manipulated so that method calls can be chained.
133 | * If you want to remove an element from an object or array and return the removed element
134 | * as a result of the operation, call {@link #atDel(int)} or {@link #atDel(String)} instead.
135 | *
138 | * If you want to add properties to an object in bulk or append a sequence of elements to array, 139 | * use the {@link #with(Json, Json...opts)} method. When used on an object, this method expects another 140 | * object as its argument and it will copy all properties of that argument into itself. Similarly, 141 | * when called on array, the method expects another array and it will append all elements of its 142 | * argument to itself. 143 | *
144 | * 145 | *146 | * To make a clone of a Json object, use the {@link #dup()} method. This method will create a new 147 | * object even for the immutable primitive Json types. Objects and arrays are cloned 148 | * (i.e. duplicated) recursively. 149 | *
150 | * 151 | *154 | * The {@link #at(int)} method returns the array element at the specified index and the 155 | * {@link #at(String)} method does the same for a property of an object instance. You can 156 | * use the {@link #at(String, Object)} version to create an object property with a default 157 | * value if it doesn't exist already. 158 | *
159 | * 160 | *161 | * To test just whether a Json object has a given property, use the {@link #has(String)} method. To test 162 | * whether a given object property or an array elements is equal to a particular value, use the 163 | * {@link #is(String, Object)} and {@link #is(int, Object)} methods respectively. Those methods return 164 | * true if the given named property (or indexed element) is equal to the passed in Object as the second 165 | * parameter. They return false if an object doesn't have the specified property or an index array is out 166 | * of bounds. For example is(name, value) is equivalent to 'has(name) && at(name).equals(make(value))'. 167 | *
168 | * 169 | *170 | * To help in navigating JSON structures, instances of this class contain a reference to the 171 | * enclosing JSON entity (object or array) if any. The enclosing entity can be accessed 172 | * with {@link #up()} method. 173 | *
174 | * 175 | *
176 | * The combination of method chaining when modifying Json instances and
177 | * the ability to navigate "inside" a structure and then go back to the enclosing
178 | * element lets one accomplish a lot in a single Java statement, without the need
179 | * of intermediary variables. Here for example how the following JSON structure can
180 | * be created in one statement using chained calls:
181 | *
184 | * {"menu": {
185 | * "id": "file",
186 | * "value": "File",
187 | * "popup": {
188 | * "menuitem": [
189 | * {"value": "New", "onclick": "CreateNewDoc()"},
190 | * {"value": "Open", "onclick": "OpenDoc()"},
191 | * {"value": "Close", "onclick": "CloseDoc()"}
192 | * ]
193 | * }
194 | * "position": 0
195 | * }}
196 | *
197 | *
198 | *
199 | * import mjson.Json;
200 | * import static mjson.Json.*;
201 | * ...
202 | * Json j = object()
203 | * .at("menu", object())
204 | * .set("id", "file")
205 | * .set("value", "File")
206 | * .at("popup", object())
207 | * .at("menuitem", array())
208 | * .add(object("value", "New", "onclick", "CreateNewDoc()"))
209 | * .add(object("value", "Open", "onclick", "OpenDoc()"))
210 | * .add(object("value", "Close", "onclick", "CloseDoc()"))
211 | * .up()
212 | * .up()
213 | * .set("position", 0)
214 | * .up();
215 | * ...
216 | *
217 | *
218 | *
219 | * If there's no danger of naming conflicts, a static import of the factory methods (
220 | * import static json.Json.*;) would reduce typing even further and make the code more
221 | * readable.
222 | *
227 | * To get a compact string representation, simply use the {@link #toString()} method. If you 228 | * want to wrap it in a JavaScript callback (for JSON with padding), use the {@link #pad(String)} 229 | * method. 230 | *
231 | * 232 | *
235 | * Since version 1.3, mJson supports JSON Schema, draft 4. A schema is represented by the internal
236 | * class {@link mjson.Json.Schema}. To perform a validation, you have a instantiate a Json.Schema
237 | * using the factory method {@link mjson.Json.Schema} and then call its validate method
238 | * on a JSON instance:
239 | *
242 | * import mjson.Json;
243 | * import static mjson.Json.*;
244 | * ...
245 | * Json inputJson = Json.read(inputString);
246 | * Json schema = Json.schema(new URI("http://mycompany.com/schemas/model"));
247 | * Json errors = schema.validate(inputJson);
248 | * for (Json error : errors.asJsonList())
249 | * System.out.println("Validation error " + err);
250 | *
251 | * @author Borislav Iordanov
252 | * @version 1.4
253 | */
254 | public class Json implements java.io.Serializable
255 | {
256 | private static final long serialVersionUID = 1L;
257 |
258 | /**
259 | *
260 | * This interface defines how Json instances are constructed. There is a
261 | * default implementation for each kind of Json value, but you can provide
262 | * your own implementation. For example, you might want a different representation of
263 | * an object than a regular HashMap. Or you might want string comparison to be
264 | * case insensitive.
265 | *
268 | * In addition, the {@link #make(Object)} method allows you plug-in your own mapping
269 | * of arbitrary Java objects to Json instances. You might want to implement
270 | * a Java Beans to JSON mapping or any other JSON serialization that makes sense in your
271 | * project.
272 | *
275 | * To avoid implementing all methods in that interface, you can extend the {@link DefaultFactory} 276 | * default implementation and simply overwrite the ones you're interested in. 277 | *
278 | * 279 | *
280 | * The factory implementation used by the Json classes is specified simply by calling
281 | * the {@link #setGlobalFactory(Factory)} method. The factory is a static, global variable by default.
282 | * If you need different factories in different areas of a single application, you may attach them
283 | * to different threads of execution using the {@link #attachFactory(Factory)}. Recall a separate
284 | * copy of static variables is made per ClassLoader, so for example in a web application context, that
285 | * global factory can be different for each web application (as Java web servers usually use a separate
286 | * class loader per application). Thread-local factories are really a provision for special cases.
287 | *
null. Implementations are
296 | * free to cache a return the same instance. The resulting value must return
297 | * true from isNull() and null from
298 | * getValue().
299 | *
300 | * @return The representation of a JSON null value.
301 | */
302 | Json nil();
303 |
304 | /**
305 | * Construct and return a JSON boolean. The resulting value must return
306 | * true from isBoolean() and the passed
307 | * in parameter from getValue().
308 | * @param value The boolean value.
309 | * @return A JSON with isBoolean() == true. Implementations
310 | * are free to cache and return the same instance for true and false.
311 | */
312 | Json bool(boolean value);
313 |
314 | /**
315 | * Construct and return a JSON string. The resulting value must return
316 | * true from isString() and the passed
317 | * in parameter from getValue().
318 | * @param value The string to wrap as a JSON value.
319 | * @return A JSON element with the given string as a value.
320 | */
321 | Json string(String value);
322 |
323 | /**
324 | * Construct and return a JSON number. The resulting value must return
325 | * true from isNumber() and the passed
326 | * in parameter from getValue().
327 | *
328 | * @param value The numeric value.
329 | * @return Json instance representing that value.
330 | */
331 | Json number(Number value);
332 |
333 | /**
334 | * Construct and return a JSON object. The resulting value must return
335 | * true from isObject() and an implementation
336 | * of java.util.Map from getValue().
337 | *
338 | * @return An empty JSON object.
339 | */
340 | Json object();
341 |
342 | /**
343 | * Construct and return a JSON object. The resulting value must return
344 | * true from isArray() and an implementation
345 | * of java.util.List from getValue().
346 | *
347 | * @return An empty JSON array.
348 | */
349 | Json array();
350 |
351 | /**
352 | * Construct and return a JSON object. The resulting value can be of any
353 | * JSON type. The method is responsible for examining the type of its
354 | * argument and performing an appropriate mapping to a Json
355 | * instance.
356 | *
357 | * @param anything An arbitray Java object from which to construct a Json
358 | * element.
359 | * @return The newly constructed Json instance.
360 | */
361 | Json make(Object anything);
362 | }
363 |
364 | /**
365 | * 366 | * Represents JSON schema - a specific data format that a JSON entity must 367 | * follow. The idea of a JSON schema is very similar to XML. Its main purpose 368 | * is validating input. 369 | *
370 | * 371 | *372 | * More information about the various JSON schema specifications can be 373 | * found at http://json-schema.org. JSON Schema is an IETF draft (v4 currently) and 374 | * our implementation follows this set of specifications. A JSON schema is specified 375 | * as a JSON object that contains keywords defined by the specification. Here are 376 | * a few introductory materials: 377 | *
391 | * Validate a JSON document according to this schema. The validations attempts to
392 | * proceed even in the face of errors. The return value is always a Json.object
393 | * containing the boolean property ok. When ok is true,
394 | * the return object contains nothing else. When it is false, the return object
395 | * contains a property errors which is an array of error messages for all
396 | * detected schema violations.
397 | *
{"ok":true} or {"ok":false, errors:["msg1", "msg2", ...]}
401 | */
402 | Json validate(Json document);
403 |
404 | /**
405 | * Possible options are: ignoreDefaults:true|false.
406 | *
Json conforming to this schema.
408 | */
409 | //Json generate(Json options);
410 | }
411 |
412 | static String fetchContent(URL url)
413 | {
414 | java.io.Reader reader = null;
415 | try
416 | {
417 | reader = new java.io.InputStreamReader((java.io.InputStream)url.getContent());
418 | StringBuilder content = new StringBuilder();
419 | char [] buf = new char[1024];
420 | for (int n = reader.read(buf); n > -1; n = reader.read(buf))
421 | content.append(buf, 0, n);
422 | // System.out.println("last reaad: " + new StringBuilder(buf))
423 | return content.toString();
424 | }
425 | catch (Exception ex)
426 | {
427 | throw new RuntimeException(ex);
428 | }
429 | finally
430 | {
431 | if (reader != null) try { reader.close(); } catch (Throwable t) { }
432 | }
433 | }
434 |
435 | static Json resolvePointer(String pointerRepresentation, Json top)
436 | {
437 | String [] parts = pointerRepresentation.split("/");
438 | Json result = top;
439 | for (String p : parts)
440 | {
441 | // TODO: unescaping and decoding
442 | if (p.length() == 0)
443 | continue;
444 | p = p.replace("~1", "/").replace("~0", "~");
445 | if (result.isArray())
446 | result = result.at(Integer.parseInt(p));
447 | else if (result.isObject())
448 | result = result.at(p);
449 | else
450 | throw new RuntimeException("Can't resolve pointer " + pointerRepresentation +
451 | " on document " + top.toString(200));
452 | }
453 | return result;
454 | }
455 |
456 | static URI makeAbsolute(URI base, String ref) throws Exception
457 | {
458 | URI refuri;
459 | if (base != null && base.getAuthority() != null && !new URI(ref).isAbsolute())
460 | {
461 | StringBuilder sb = new StringBuilder();
462 | if (base.getScheme() != null)
463 | sb.append(base.getScheme()).append("://");
464 | sb.append(base.getAuthority());
465 | if (!ref.startsWith("/"))
466 | {
467 | if (ref.startsWith("#"))
468 | sb.append(base.getPath());
469 | else
470 | {
471 | int slashIdx = base.getPath().lastIndexOf('/');
472 | sb.append(slashIdx == -1 ? base.getPath() : base.getPath().substring(0, slashIdx)).append("/");
473 | }
474 | }
475 | refuri = new URI(sb.append(ref).toString());
476 | }
477 | else if (base != null)
478 | refuri = base.resolve(ref);
479 | else
480 | refuri = new URI(ref);
481 | return refuri;
482 | }
483 |
484 | static Json resolveRef(URI base,
485 | Json refdoc,
486 | URI refuri,
487 | Map517 | * Replace all JSON references, as per the http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 518 | * specification, by their referants. 519 | *
520 | * @param json 521 | * @param duplicate 522 | * @param done 523 | * @return 524 | */ 525 | static Json expandReferences(Json json, 526 | Json topdoc, 527 | URI base, 528 | MapReturn the {@link Factory} currently in effect. This is the factory that the {@link #make(Object)} method 1160 | * will dispatch on upon determining the type of its argument. If you already know the type 1161 | * of element to construct, you can avoid the type introspection implicit to the make method 1162 | * and call the factory directly. This will result in an optimization.
1163 | * 1164 | * @return the factory 1165 | */ 1166 | public static Factory factory() 1167 | { 1168 | Factory f = threadFactory.get(); 1169 | return f != null ? f : globalFactory; 1170 | } 1171 | 1172 | /** 1173 | *1174 | * Specify a global Json {@link Factory} to be used by all threads that don't have a 1175 | * specific thread-local factory attached to them. 1176 | *
1177 | * 1178 | * @param factory The new global factory 1179 | */ 1180 | public static void setGlobalFactory(Factory factory) { globalFactory = factory; } 1181 | 1182 | /** 1183 | *1184 | * Attach a thread-local Json {@link Factory} to be used specifically by this thread. Thread-local 1185 | * Json factories are the only means to have different {@link Factory} implementations used simultaneously 1186 | * in the same application (well, more accurately, the same ClassLoader). 1187 | *
1188 | * 1189 | * @param factory the new thread local factory 1190 | */ 1191 | public static void attachFactory(Factory factory) { threadFactory.set(factory); } 1192 | 1193 | /** 1194 | *1195 | * Clear the thread-local factory previously attached to this thread via the 1196 | * {@link #attachFactory(Factory)} method. The global factory takes effect after 1197 | * a call to this method. 1198 | *
1199 | */ 1200 | public static void detachFactory() { threadFactory.remove(); } 1201 | 1202 | /** 1203 | *1204 | * Parse a JSON entity from its string representation. 1205 | *
1206 | * 1207 | * @param jsonAsString A valid JSON representation as per the json.org 1208 | * grammar. Cannot benull.
1209 | * @return The JSON entity parsed: an object, array, string, number or boolean, or null. Note that
1210 | * this method will never return the actual Java null.
1211 | */
1212 | public static Json read(String jsonAsString) { return (Json)new Reader().read(jsonAsString); }
1213 |
1214 | /**
1215 | *
1216 | * Parse a JSON entity from a URL.
1217 | *
null.
1220 | * @return The JSON entity parsed: an object, array, string, number or boolean, or null. Note that
1221 | * this method will never return the actual Java null.
1222 | */
1223 | public static Json read(URL location) { return (Json)new Reader().read(fetchContent(location)); }
1224 |
1225 | /**
1226 | * 1227 | * Parse a JSON entity from a {@link CharacterIterator}. 1228 | *
1229 | * @param it A character iterator. 1230 | * @return the parsed JSON element 1231 | * @see #read(String) 1232 | */ 1233 | public static Json read(CharacterIterator it) { return (Json)new Reader().read(it); } 1234 | /** 1235 | * @return thenull Json instance.
1236 | */
1237 | public static Json nil() { return factory().nil(); }
1238 | /**
1239 | * @return a newly constructed, empty JSON object.
1240 | */
1241 | public static Json object() { return factory().object(); }
1242 | /**
1243 | * Return a new JSON object initialized from the passed list of
1244 | * name/value pairs. The number of arguments must
1245 | * be even. Each argument at an even position is taken to be a name
1246 | * for the following value. The name arguments are normally of type
1247 | * Java String, but they can be of any other type having an appropriate
1248 | * toString method. Each value is first converted
1249 | * to a Json instance using the {@link #make(Object)} method.
1250 | *
Return a new JSON array filled up with the list of arguments.
1271 | * 1272 | * @param args The initial content of the array. 1273 | * @return the new JSON array 1274 | */ 1275 | public static Json array(Object...args) 1276 | { 1277 | Json A = array(); 1278 | for (Object x : args) 1279 | A.add(factory().make(x)); 1280 | return A; 1281 | } 1282 | 1283 | /** 1284 | *1285 | * Convert an arbitrary Java instance to a {@link Json} instance. 1286 | *
1287 | * 1288 | *
1289 | * Maps, Collections and arrays are recursively copied where each of
1290 | * their elements concerted into Json instances as well. The keys
1291 | * of a {@link Map} parameter are normally strings, but anything with a meaningful
1292 | * toString implementation will work as well.
1293 | *
Json. This method will never return null. It will
1297 | * throw an {@link IllegalArgumentException} if it doesn't know how to convert the argument
1298 | * to a Json instance.
1299 | * @throws IllegalArgumentException when the concrete type of the parameter is
1300 | * unknown.
1301 | */
1302 | public static Json make(Object anything)
1303 | {
1304 | return factory().make(anything);
1305 | }
1306 |
1307 | // end of static utility method section
1308 |
1309 | Json enclosing = null;
1310 |
1311 | protected Json() { }
1312 | protected Json(Json enclosing) { this.enclosing = enclosing; }
1313 |
1314 | /**
1315 | * Return a string representation of this that does
1316 | * not exceed a certain maximum length. This is useful in constructing
1317 | * error messages or any other place where only a "preview" of the
1318 | * JSON element should be displayed. Some JSON structures can get
1319 | * very large and this method will help avoid string serializing
1320 | * the whole of them.
Explicitly set the parent of this element. The parent is presumably an array 1329 | * or an object. Normally, there's no need to call this method as the parent is 1330 | * automatically set by the framework. You may need to call it however, if you implement 1331 | * your own {@link Factory} with your own implementations of the Json types. 1332 | *
1333 | * 1334 | * @param enclosing The parent element. 1335 | */ 1336 | public void attachTo(Json enclosing) { this.enclosing = enclosing; } 1337 | 1338 | /** 1339 | * @return theJson entity, if any, enclosing this
1340 | * Json. The returned value can be null or
1341 | * a Json object or list, but not one of the primitive types.
1342 | */
1343 | public final Json up() { return enclosing; }
1344 |
1345 | /**
1346 | * @return a clone (a duplicate) of this Json entity. Note that cloning
1347 | * is deep if array and objects. Primitives are also cloned, even though their values are immutable
1348 | * because the new enclosing entity (the result of the {@link #up()} method) may be different.
1349 | * since they are immutable.
1350 | */
1351 | public Json dup() { return this; }
1352 |
1353 | /**
1354 | * Return the Json element at the specified index of this
1355 | * Json array. This method applies only to Json arrays.
1356 | *
1365 | * Return the specified property of a Json object or null
1366 | * if there's no such property. This method applies only to Json objects.
1367 | *
1375 | * Return the specified property of a Json object if it exists.
1376 | * If it doesn't, then create a new property with value the def
1377 | * parameter and return that parameter.
1378 | *
1397 | * Return the specified property of a Json object if it exists.
1398 | * If it doesn't, then create a new property with value the def
1399 | * parameter and return that parameter.
1400 | *
1412 | * Return true if this Json object has the specified property
1413 | * and false otherwise.
1414 | *
1422 | * Return true if and only if this Json object has a property with
1423 | * the specified value. In particular, if the object has no such property false is returned.
1424 | *
Json, it is first converted to
1429 | * such an instance.
1430 | * @return
1431 | */
1432 | public boolean is(String property, Object value) { throw new UnsupportedOperationException(); }
1433 |
1434 | /**
1435 | *
1436 | * Return true if and only if this Json array has an element with
1437 | * the specified value at the specified index. In particular, if the array has no element at
1438 | * this index, false is returned.
1439 | *
Json, it is first converted to
1444 | * such an instance.
1445 | * @return
1446 | */
1447 | public boolean is(int index, Object value) { throw new UnsupportedOperationException(); }
1448 |
1449 | /**
1450 | *
1451 | * Add the specified Json element to this array.
1452 | *
1460 | * Add an arbitrary Java object to this Json array. The object
1461 | * is first converted to a Json instance by calling the static
1462 | * {@link #make} method.
1463 | *
1472 | * Remove the specified property from a Json object and return
1473 | * that property.
1474 | *
null if the object didn't have such
1478 | * a property to begin with.
1479 | */
1480 | public Json atDel(String property) { throw new UnsupportedOperationException(); }
1481 |
1482 | /**
1483 | *
1484 | * Remove the element at the specified index from a Json array and return
1485 | * that element.
1486 | *
1495 | * Delete the specified property from a Json object.
1496 | *
1505 | * Remove the element at the specified index from a Json array.
1506 | *
1515 | * Remove the specified element from a Json array.
1516 | *
1525 | * Remove the specified Java object (converted to a Json instance)
1526 | * from a Json array. This is equivalent to
1527 | * remove({@link #make(Object)}).
1528 | *
1537 | * Set a Json objects's property.
1538 | *
1548 | * Set a Json objects's property.
1549 | *
Json representation
1553 | * with {@link #make}.
1554 | * @return this
1555 | */
1556 | public final Json set(String property, Object value) { return set(property, make(value)); }
1557 |
1558 | /**
1559 | * 1560 | * Change the value of a JSON array element. This must be an array. 1561 | *
1562 | * @param index 0-based index of the element in the array. 1563 | * @param value the new value of the element 1564 | * @return this 1565 | */ 1566 | public Json set(int index, Object value) { throw new UnsupportedOperationException(); } 1567 | 1568 | /** 1569 | *
1570 | * Combine this object or array with the passed in object or array. The types of
1571 | * this and the object argument must match. If both are
1572 | * Json objects, all properties of the parameter are added to this.
1573 | * If both are arrays, all elements of the parameter are appended to this
1574 | *
{}@link #with(Json,Json...options)} with each option
1583 | * argument converted to Json first.
1584 | */
1585 | public Json with(Json object, Object...options)
1586 | {
1587 | Json [] jopts = new Json[options.length];
1588 | for (int i = 0; i < jopts.length; i++)
1589 | jopts[i] = make(options[i]);
1590 | return with(object, jopts);
1591 | }
1592 |
1593 | /**
1594 | * @return the underlying value of this Json entity. The actual value will
1595 | * be a Java Boolean, String, Number, Map, List or null. For complex entities (objects
1596 | * or arrays), the method will perform a deep copy and extra underlying values recursively
1597 | * for all nested elements.
1598 | */
1599 | public Object getValue() { throw new UnsupportedOperationException(); }
1600 |
1601 | /**
1602 | * @return the boolean value of a boolean Json instance. Call
1603 | * {@link #isBoolean()} first if you're not sure this instance is indeed a
1604 | * boolean.
1605 | */
1606 | public boolean asBoolean() { throw new UnsupportedOperationException(); }
1607 |
1608 | /**
1609 | * @return the string value of a string Json instance. Call
1610 | * {@link #isString()} first if you're not sure this instance is indeed a
1611 | * string.
1612 | */
1613 | public String asString() { throw new UnsupportedOperationException(); }
1614 |
1615 | /**
1616 | * @return the integer value of a number Json instance. Call
1617 | * {@link #isNumber()} first if you're not sure this instance is indeed a
1618 | * number.
1619 | */
1620 | public int asInteger() { throw new UnsupportedOperationException(); }
1621 |
1622 | /**
1623 | * @return the float value of a float Json instance. Call
1624 | * {@link #isNumber()} first if you're not sure this instance is indeed a
1625 | * number.
1626 | */
1627 | public float asFloat() { throw new UnsupportedOperationException(); }
1628 |
1629 | /**
1630 | * @return the double value of a number Json instance. Call
1631 | * {@link #isNumber()} first if you're not sure this instance is indeed a
1632 | * number.
1633 | */
1634 | public double asDouble() { throw new UnsupportedOperationException(); }
1635 |
1636 | /**
1637 | * @return the long value of a number Json instance. Call
1638 | * {@link #isNumber()} first if you're not sure this instance is indeed a
1639 | * number.
1640 | */
1641 | public long asLong() { throw new UnsupportedOperationException(); }
1642 |
1643 | /**
1644 | * @return the short value of a number Json instance. Call
1645 | * {@link #isNumber()} first if you're not sure this instance is indeed a
1646 | * number.
1647 | */
1648 | public short asShort() { throw new UnsupportedOperationException(); }
1649 |
1650 | /**
1651 | * @return the byte value of a number Json instance. Call
1652 | * {@link #isNumber()} first if you're not sure this instance is indeed a
1653 | * number.
1654 | */
1655 | public byte asByte() { throw new UnsupportedOperationException(); }
1656 |
1657 | /**
1658 | * @return the first character of a string Json instance. Call
1659 | * {@link #isString()} first if you're not sure this instance is indeed a
1660 | * string.
1661 | */
1662 | public char asChar() { throw new UnsupportedOperationException(); }
1663 |
1664 | /**
1665 | * @return a map of the properties of an object Json instance. The map
1666 | * is a clone of the object and can be modified safely without affecting it. Call
1667 | * {@link #isObject()} first if you're not sure this instance is indeed a
1668 | * Json object.
1669 | */
1670 | public MapJson object. The returned
1674 | * map is the actual object representation so any modifications to it are modifications
1675 | * of the Json object itself. Call
1676 | * {@link #isObject()} first if you're not sure this instance is indeed a
1677 | * Json object.
1678 | */
1679 | public MapJson array. The list is a clone
1683 | * of the array and can be modified safely without affecting it. Call
1684 | * {@link #isArray()} first if you're not sure this instance is indeed a
1685 | * Json array.
1686 | */
1687 | public List