001/* ============ 002 * Orson Charts 003 * ============ 004 * 005 * (C)opyright 2013, 2014, by Object Refinery Limited. 006 * 007 * http://www.object-refinery.com/orsoncharts/index.html 008 * 009 * JSON.simple 010 * ----------- 011 * The code in this file originates from the JSON.simple project by 012 * FangYidong<fangyidong@yahoo.com.cn>: 013 * 014 * https://code.google.com/p/json-simple/ 015 * 016 * which is licensed under the Apache Software License version 2.0. 017 * 018 * It has been modified locally and repackaged under 019 * com.orsoncharts.util.json.* to avoid conflicts with any other version that 020 * may be present on the classpath. 021 * 022 */ 023 024package com.orsoncharts.util.json; 025 026import java.io.IOException; 027import java.io.Reader; 028import java.io.StringReader; 029import java.io.Writer; 030import java.util.List; 031import java.util.Map; 032import com.orsoncharts.util.json.parser.JSONParser; 033import com.orsoncharts.util.json.parser.ParseException; 034 035/** 036 * Utility methods for JSON values. 037 */ 038public class JSONValue { 039 040 /** 041 * Parse JSON text into java object from the input source. 042 * Please use parseWithException() if you don't want to ignore the 043 * exception. 044 * 045 * @see com.orsoncharts.util.json.parser.JSONParser#parse(Reader) 046 * @see #parseWithException(Reader) 047 * 048 * @param in the input reader. 049 * @return Instance of the following: 050 * com.orsoncharts.util.json.JSONObject, 051 * com.orsoncharts.util.json.JSONArray, 052 * java.lang.String, 053 * java.lang.Number, 054 * java.lang.Boolean, 055 * null 056 */ 057 public static Object parse(Reader in){ 058 try { 059 JSONParser parser = new JSONParser(); 060 return parser.parse(in); 061 } 062 catch (Exception e) { 063 return null; 064 } 065 } 066 067 /** 068 * Parses an object from a string. 069 * 070 * @param s the string. 071 * 072 * @return An object. 073 */ 074 public static Object parse(String s){ 075 StringReader in = new StringReader(s); 076 return parse(in); 077 } 078 079 /** 080 * Parse JSON text into java object from the input source. 081 * 082 * @see com.orsoncharts.util.json.parser.JSONParser 083 * 084 * @param in the input reader ({@code null} not permitted). 085 * 086 * @return Instance of the following: 087 * com.orsoncharts.util.json.JSONObject, 088 * com.orsoncharts.util.json.JSONArray, 089 * java.lang.String, 090 * java.lang.Number, 091 * java.lang.Boolean, 092 * null 093 * 094 * @throws IOException if there is an I/O problem. 095 * @throws ParseException if there is a parsing problem. 096 */ 097 public static Object parseWithException(Reader in) throws IOException, 098 ParseException{ 099 JSONParser parser = new JSONParser(); 100 return parser.parse(in); 101 } 102 103 /** 104 * Parses an object from a JSON string. 105 * 106 * @param s the string. 107 * 108 * @return An object. 109 * 110 * @throws ParseException if there is a parsing problem. 111 */ 112 public static Object parseWithException(String s) throws ParseException{ 113 JSONParser parser = new JSONParser(); 114 return parser.parse(s); 115 } 116 117 /** 118 * Encode an object into JSON text and write it to out. 119 * <p> 120 * If this object is a {@code Map} or a {@code List}, and it's 121 * also a {@link JSONStreamAware} or a {@link JSONAware}, 122 * {@code JSONStreamAware} or {@code JSONAware} will be 123 * considered firstly. 124 * <p> 125 * DO NOT call this method from writeJSONString(Writer) of a class that 126 * implements both JSONStreamAware and (Map or List) with 127 * "this" as the first parameter, use JSONObject.writeJSONString(Map, 128 * Writer) or JSONArray.writeJSONString(List, Writer) instead. 129 * 130 * @see com.orsoncharts.util.json.JSONObject#writeJSONString(Map, Writer) 131 * @see com.orsoncharts.util.json.JSONArray#writeJSONString(List, Writer) 132 * 133 * @param value the value. 134 * @param out the output writer. 135 * @throws IOException if there is an I/O problem. 136 */ 137 public static void writeJSONString(Object value, Writer out) 138 throws IOException { 139 if (value == null) { 140 out.write("null"); 141 return; 142 } 143 144 if (value instanceof String) { 145 out.write('\"'); 146 out.write(escape((String) value)); 147 out.write('\"'); 148 return; 149 } 150 151 if (value instanceof Double) { 152 if(((Double) value).isInfinite() || ((Double) value).isNaN()) { 153 out.write("null"); 154 } 155 else { 156 out.write(value.toString()); 157 } 158 return; 159 } 160 161 if (value instanceof Float) { 162 if (((Float) value).isInfinite() || ((Float) value).isNaN()) { 163 out.write("null"); 164 } 165 else { 166 out.write(value.toString()); 167 } 168 return; 169 } 170 171 if (value instanceof Number) { 172 out.write(value.toString()); 173 return; 174 } 175 176 if (value instanceof Boolean) { 177 out.write(value.toString()); 178 return; 179 } 180 181 if ((value instanceof JSONStreamAware)) { 182 ((JSONStreamAware) value).writeJSONString(out); 183 return; 184 } 185 186 if ((value instanceof JSONAware)) { 187 out.write(((JSONAware) value).toJSONString()); 188 return; 189 } 190 191 if (value instanceof Map) { 192 JSONObject.writeJSONString((Map) value, out); 193 return; 194 } 195 196 if (value instanceof List) { 197 JSONArray.writeJSONString((List) value, out); 198 return; 199 } 200 201 out.write(value.toString()); 202 } 203 204 /** 205 * Convert an object to JSON text. 206 * <p> 207 * If this object is a Map or a List, and it's also a JSONAware, JSONAware 208 * will be considered firstly. 209 * <p> 210 * DO NOT call this method from toJSONString() of a class that implements 211 * both JSONAware and Map or List with 212 * "this" as the parameter, use JSONObject.toJSONString(Map) or 213 * JSONArray.toJSONString(List) instead. 214 * 215 * @see com.orsoncharts.util.json.JSONObject#toJSONString(Map) 216 * @see com.orsoncharts.util.json.JSONArray#toJSONString(List) 217 * 218 * @param value the value. 219 * @return JSON text, or "null" if value is null or it's an NaN or an INF 220 * number. 221 */ 222 public static String toJSONString(Object value){ 223 if (value == null) { 224 return "null"; 225 } 226 227 if (value instanceof String) { 228 return "\"" + escape((String) value) + "\""; 229 } 230 231 if (value instanceof Double){ 232 if(((Double) value).isInfinite() || ((Double) value).isNaN()) { 233 return "null"; 234 } 235 else { 236 return value.toString(); 237 } 238 } 239 240 if (value instanceof Float) { 241 if (((Float) value).isInfinite() || ((Float) value).isNaN()) { 242 return "null"; 243 } 244 else { 245 return value.toString(); 246 } 247 } 248 249 if (value instanceof Number) { 250 return value.toString(); 251 } 252 253 if (value instanceof Boolean) { 254 return value.toString(); 255 } 256 257 if ((value instanceof JSONAware)) { 258 return ((JSONAware) value).toJSONString(); 259 } 260 261 if (value instanceof Map) { 262 return JSONObject.toJSONString((Map) value); 263 } 264 265 if (value instanceof List) { 266 return JSONArray.toJSONString((List) value); 267 } 268 269 return value.toString(); 270 } 271 272 /** 273 * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters 274 * (U+0000 through U+001F). 275 * 276 * @param s the string to be escaped ({@code null} permitted). 277 * 278 * @return A string. 279 */ 280 public static String escape(String s) { 281 if (s == null) { 282 return null; 283 } 284 StringBuffer sb = new StringBuffer(); 285 escape(s, sb); 286 return sb.toString(); 287 } 288 289 /** 290 * @param s - Must not be null. 291 * @param sb 292 */ 293 static void escape(String s, StringBuffer sb) { 294 for(int i = 0; i < s.length(); i++) { 295 char ch = s.charAt(i); 296 switch(ch){ 297 case '"': 298 sb.append("\\\""); 299 break; 300 case '\\': 301 sb.append("\\\\"); 302 break; 303 case '\b': 304 sb.append("\\b"); 305 break; 306 case '\f': 307 sb.append("\\f"); 308 break; 309 case '\n': 310 sb.append("\\n"); 311 break; 312 case '\r': 313 sb.append("\\r"); 314 break; 315 case '\t': 316 sb.append("\\t"); 317 break; 318 case '/': 319 sb.append("\\/"); 320 break; 321 default: 322 //Reference: http://www.unicode.org/versions/Unicode5.1.0/ 323 if ((ch >= '\u0000' && ch <= '\u001F') 324 || (ch >= '\u007F' && ch <= '\u009F') 325 || (ch >= '\u2000' && ch <= '\u20FF')) { 326 String ss = Integer.toHexString(ch); 327 sb.append("\\u"); 328 for (int k = 0; k < 4 - ss.length(); k++) { 329 sb.append('0'); 330 } 331 sb.append(ss.toUpperCase()); 332 } 333 else { 334 sb.append(ch); 335 } 336 } 337 }//for 338 } 339 340}