001/******************************************************************************* 002 * Copyright (c) 2015, 2016 EclipseSource. 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining a copy 005 * of this software and associated documentation files (the "Software"), to deal 006 * in the Software without restriction, including without limitation the rights 007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 * copies of the Software, and to permit persons to whom the Software is 009 * furnished to do so, subject to the following conditions: 010 * 011 * The above copyright notice and this permission notice shall be included in all 012 * copies or substantial portions of the Software. 013 * 014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 020 * SOFTWARE. 021 ******************************************************************************/ 022package com.restfb.json; 023 024import java.io.IOException; 025import java.io.Reader; 026import java.util.Arrays; 027import java.util.Objects; 028 029/** 030 * This class serves as the entry point to the minimal-json API. 031 * <p> 032 * To <strong>parse</strong> a given JSON input, use the <code>parse()</code> methods like in this example: 033 * </p> 034 * 035 * <pre> 036 * JsonObject object = Json.parse(string).asObject(); 037 * </pre> 038 * <p> 039 * To <strong>create</strong> a JSON data structure to be serialized, use the methods <code>value()</code>, 040 * <code>array()</code>, and <code>object()</code>. For example, the following snippet will produce the JSON string 041 * <em>{"foo": 23, "bar": true}</em>: 042 * </p> 043 * 044 * <pre> 045 * String string = Json.object().add("foo", 23).add("bar", true).toString(); 046 * </pre> 047 * <p> 048 * To create a JSON array from a given Java array, you can use one of the <code>array()</code> methods with varargs 049 * parameters: 050 * </p> 051 * 052 * <pre> 053 * String[] names = ... 054 * JsonArray array = Json.array(names); 055 * </pre> 056 */ 057public final class Json { 058 059 // String constants 060 private static final String VALUES_IS_NULL = "values is null"; 061 private static final String STRING_IS_NULL = "string is null"; 062 private static final String READER_IS_NULL = "reader is null"; 063 private static final String INFINITE_AND_NAN = "Infinite and NaN values not permitted in JSON"; 064 065 private Json() { 066 // not meant to be instantiated 067 } 068 069 /** 070 * Represents the JSON literal <code>null</code>. 071 */ 072 public static final JsonValue NULL = new JsonLiteral("null"); 073 074 /** 075 * Represents the JSON literal <code>true</code>. 076 */ 077 public static final JsonValue TRUE = new JsonLiteral("true"); 078 079 /** 080 * Represents the JSON literal <code>false</code>. 081 */ 082 public static final JsonValue FALSE = new JsonLiteral("false"); 083 084 /** 085 * Returns a JsonValue instance that represents the given <code>int</code> value. 086 * 087 * @param value 088 * the value to get a JSON representation for 089 * @return a JSON value that represents the given value 090 */ 091 public static JsonValue value(int value) { 092 return new JsonNumber(Integer.toString(value, 10)); 093 } 094 095 /** 096 * Returns a JsonValue instance that represents the given <code>long</code> value. 097 * 098 * @param value 099 * the value to get a JSON representation for 100 * @return a JSON value that represents the given value 101 */ 102 public static JsonValue value(long value) { 103 return new JsonNumber(Long.toString(value, 10)); 104 } 105 106 /** 107 * Returns a JsonValue instance that represents the given <code>float</code> value. 108 * 109 * @param value 110 * the value to get a JSON representation for 111 * @return a JSON value that represents the given value 112 */ 113 public static JsonValue value(float value) { 114 if (Float.isInfinite(value) || Float.isNaN(value)) { 115 throw new IllegalArgumentException(INFINITE_AND_NAN); 116 } 117 return new JsonNumber(cutOffPointZero(Float.toString(value))); 118 } 119 120 /** 121 * Returns a JsonValue instance that represents the given <code>double</code> value. 122 * 123 * @param value 124 * the value to get a JSON representation for 125 * @return a JSON value that represents the given value 126 */ 127 public static JsonValue value(double value) { 128 if (Double.isInfinite(value) || Double.isNaN(value)) { 129 throw new IllegalArgumentException(INFINITE_AND_NAN); 130 } 131 return new JsonNumber(cutOffPointZero(Double.toString(value))); 132 } 133 134 /** 135 * Returns a JsonValue instance that represents the given string. 136 * 137 * @param string 138 * the string to get a JSON representation for 139 * @return a JSON value that represents the given string 140 */ 141 public static JsonValue value(String string) { 142 return string == null ? NULL : new JsonString(string); 143 } 144 145 /** 146 * Returns a JsonValue instance that represents the given <code>boolean</code> value. 147 * 148 * @param value 149 * the value to get a JSON representation for 150 * @return a JSON value that represents the given value 151 */ 152 public static JsonValue value(boolean value) { 153 return value ? TRUE : FALSE; 154 } 155 156 /** 157 * Creates a new empty JsonArray. This is equivalent to creating a new JsonArray using the constructor. 158 * 159 * @return a new empty JSON array 160 */ 161 public static JsonArray array() { 162 return new JsonArray(); 163 } 164 165 /** 166 * Creates a new JsonArray that contains the JSON representations of the given <code>int</code> values. 167 * 168 * @param values 169 * the values to be included in the new JSON array 170 * @return a new JSON array that contains the given values 171 */ 172 public static JsonArray array(int... values) { 173 Objects.requireNonNull(values, VALUES_IS_NULL); 174 JsonArray array = new JsonArray(); 175 Arrays.stream(values).forEach(array::add); 176 return array; 177 } 178 179 /** 180 * Creates a new JsonArray that contains the JSON representations of the given <code>long</code> values. 181 * 182 * @param values 183 * the values to be included in the new JSON array 184 * @return a new JSON array that contains the given values 185 */ 186 public static JsonArray array(long... values) { 187 Objects.requireNonNull(values, VALUES_IS_NULL); 188 JsonArray array = new JsonArray(); 189 Arrays.stream(values).forEach(array::add); 190 return array; 191 } 192 193 /** 194 * Creates a new JsonArray that contains the JSON representations of the given <code>float</code> values. 195 * 196 * @param values 197 * the values to be included in the new JSON array 198 * @return a new JSON array that contains the given values 199 */ 200 public static JsonArray array(float... values) { 201 Objects.requireNonNull(values, VALUES_IS_NULL); 202 JsonArray array = new JsonArray(); 203 for (float value : values) { 204 array.add(value); 205 } 206 return array; 207 } 208 209 /** 210 * Creates a new JsonArray that contains the JSON representations of the given <code>double</code> values. 211 * 212 * @param values 213 * the values to be included in the new JSON array 214 * @return a new JSON array that contains the given values 215 */ 216 public static JsonArray array(double... values) { 217 Objects.requireNonNull(values, VALUES_IS_NULL); 218 JsonArray array = new JsonArray(); 219 Arrays.stream(values).forEach(array::add); 220 return array; 221 } 222 223 /** 224 * Creates a new JsonArray that contains the JSON representations of the given <code>boolean</code> values. 225 * 226 * @param values 227 * the values to be included in the new JSON array 228 * @return a new JSON array that contains the given values 229 */ 230 public static JsonArray array(boolean... values) { 231 Objects.requireNonNull(values, VALUES_IS_NULL); 232 JsonArray array = new JsonArray(); 233 for (boolean value : values) { 234 array.add(value); 235 } 236 return array; 237 } 238 239 /** 240 * Creates a new JsonArray that contains the JSON representations of the given strings. 241 * 242 * @param strings 243 * the strings to be included in the new JSON array 244 * @return a new JSON array that contains the given strings 245 */ 246 public static JsonArray array(String... strings) { 247 Objects.requireNonNull(strings, VALUES_IS_NULL); 248 JsonArray array = new JsonArray(); 249 Arrays.stream(strings).forEach(array::add); 250 return array; 251 } 252 253 /** 254 * Creates a new empty JsonObject. This is equivalent to creating a new JsonObject using the constructor. 255 * 256 * @return a new empty JSON object 257 */ 258 public static JsonObject object() { 259 return new JsonObject(); 260 } 261 262 /** 263 * Parses the given input string as JSON. The input must contain a valid JSON value, optionally padded with 264 * whitespace. 265 * 266 * @param string 267 * the input string, must be valid JSON 268 * @return a value that represents the parsed JSON 269 * @throws ParseException 270 * if the input is not valid JSON 271 */ 272 public static JsonValue parse(String string) { 273 Objects.requireNonNull(string, STRING_IS_NULL); 274 DefaultHandler handler = new DefaultHandler(); 275 new JsonParser(handler).parse(string); 276 return handler.getValue(); 277 } 278 279 /** 280 * Reads the entire input from the given reader and parses it as JSON. The input must contain a 281 * valid JSON value, optionally padded with whitespace. 282 * <p> 283 * Characters are read in chunks into an input buffer. Hence, wrapping a reader in an additional 284 * <code>BufferedReader</code> likely won't improve reading performance. 285 * </p> 286 * 287 * @param reader 288 * the reader to read the JSON value from 289 * @return a value that represents the parsed JSON 290 * @throws IOException 291 * if an I/O error occurs in the reader 292 * @throws ParseException 293 * if the input is not valid JSON 294 */ 295 public static JsonValue parse(Reader reader) throws IOException { 296 Objects.requireNonNull(reader, READER_IS_NULL); 297 DefaultHandler handler = new DefaultHandler(); 298 new JsonParser(handler).parse(reader); 299 return handler.getValue(); 300 } 301 302 private static String cutOffPointZero(String string) { 303 if (string.endsWith(".0")) { 304 return string.substring(0, string.length() - 2); 305 } 306 return string; 307 } 308 309 static class DefaultHandler extends JsonHandler<JsonArray, JsonObject> { 310 311 protected JsonValue value; 312 313 @Override 314 public JsonArray startArray() { 315 return new JsonArray(); 316 } 317 318 @Override 319 public JsonObject startObject() { 320 return new JsonObject(); 321 } 322 323 @Override 324 public void endNull() { 325 value = NULL; 326 } 327 328 @Override 329 public void endBoolean(boolean bool) { 330 value = bool ? TRUE : FALSE; 331 } 332 333 @Override 334 public void endString(String string) { 335 value = new JsonString(string); 336 } 337 338 @Override 339 public void endNumber(String string) { 340 value = new JsonNumber(string); 341 } 342 343 @Override 344 public void endArray(JsonArray array) { 345 value = array; 346 } 347 348 @Override 349 public void endObject(JsonObject object) { 350 value = object; 351 } 352 353 @Override 354 public void endArrayValue(JsonArray array) { 355 array.add(value); 356 } 357 358 @Override 359 public void endObjectValue(JsonObject object, String name) { 360 object.add(name, value); 361 } 362 363 JsonValue getValue() { 364 return value; 365 } 366 367 } 368 369}