agones.dev/agones@v1.54.0/sdks/unity/third_party/MiniJSON.cs (about) 1 /* 2 * Copyright (c) 2013 Calvin Rien 3 * 4 * Based on the JSON parser by Patrick van Bergen 5 * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html 6 * 7 * Simplified it so that it doesn't throw exceptions 8 * and can be used in Unity iPhone with maximum code stripping. 9 * 10 * Permission is hereby granted, free of charge, to any person obtaining 11 * a copy of this software and associated documentation files (the 12 * "Software"), to deal in the Software without restriction, including 13 * without limitation the rights to use, copy, modify, merge, publish, 14 * distribute, sublicense, and/or sell copies of the Software, and to 15 * permit persons to whom the Software is furnished to do so, subject to 16 * the following conditions: 17 * 18 * The above copyright notice and this permission notice shall be 19 * included in all copies or substantial portions of the Software. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 */ 29 using System; 30 using System.Collections; 31 using System.Collections.Generic; 32 using System.IO; 33 using System.Text; 34 35 namespace MiniJSON { 36 // Example usage: 37 // 38 // using UnityEngine; 39 // using System.Collections; 40 // using System.Collections.Generic; 41 // using MiniJSON; 42 // 43 // public class MiniJSONTest : MonoBehaviour { 44 // void Start () { 45 // var jsonString = "{ \"array\": [1.44,2,3], " + 46 // "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + 47 // "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + 48 // "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + 49 // "\"int\": 65536, " + 50 // "\"float\": 3.1415926, " + 51 // "\"bool\": true, " + 52 // "\"null\": null }"; 53 // 54 // var dict = Json.Deserialize(jsonString) as Dictionary<string,object>; 55 // 56 // Debug.Log("deserialized: " + dict.GetType()); 57 // Debug.Log("dict['array'][0]: " + ((List<object>) dict["array"])[0]); 58 // Debug.Log("dict['string']: " + (string) dict["string"]); 59 // Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles 60 // Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs 61 // Debug.Log("dict['unicode']: " + (string) dict["unicode"]); 62 // 63 // var str = Json.Serialize(dict); 64 // 65 // Debug.Log("serialized: " + str); 66 // } 67 // } 68 69 /// <summary> 70 /// This class encodes and decodes JSON strings. 71 /// Spec. details, see http://www.json.org/ 72 /// 73 /// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary. 74 /// All numbers are parsed to doubles. 75 /// </summary> 76 public static class Json { 77 /// <summary> 78 /// Parses the string json into a value 79 /// </summary> 80 /// <param name="json">A JSON string.</param> 81 /// <returns>An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false</returns> 82 public static object Deserialize(string json) { 83 // save the string for debug information 84 if (json == null) { 85 return null; 86 } 87 88 return Parser.Parse(json); 89 } 90 91 sealed class Parser : IDisposable { 92 const string WORD_BREAK = "{}[],:\""; 93 94 public static bool IsWordBreak(char c) { 95 return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1; 96 } 97 98 enum TOKEN { 99 NONE, 100 CURLY_OPEN, 101 CURLY_CLOSE, 102 SQUARED_OPEN, 103 SQUARED_CLOSE, 104 COLON, 105 COMMA, 106 STRING, 107 NUMBER, 108 TRUE, 109 FALSE, 110 NULL 111 }; 112 113 StringReader json; 114 115 Parser(string jsonString) { 116 json = new StringReader(jsonString); 117 } 118 119 public static object Parse(string jsonString) { 120 using (var instance = new Parser(jsonString)) { 121 return instance.ParseValue(); 122 } 123 } 124 125 public void Dispose() { 126 json.Dispose(); 127 json = null; 128 } 129 130 Dictionary<string, object> ParseObject() { 131 Dictionary<string, object> table = new Dictionary<string, object>(); 132 133 // ditch opening brace 134 json.Read(); 135 136 // { 137 while (true) { 138 switch (NextToken) { 139 case TOKEN.NONE: 140 return null; 141 case TOKEN.COMMA: 142 continue; 143 case TOKEN.CURLY_CLOSE: 144 return table; 145 default: 146 // name 147 string name = ParseString(); 148 if (name == null) { 149 return null; 150 } 151 152 // : 153 if (NextToken != TOKEN.COLON) { 154 return null; 155 } 156 // ditch the colon 157 json.Read(); 158 159 // value 160 table[name] = ParseValue(); 161 break; 162 } 163 } 164 } 165 166 List<object> ParseArray() { 167 List<object> array = new List<object>(); 168 169 // ditch opening bracket 170 json.Read(); 171 172 // [ 173 var parsing = true; 174 while (parsing) { 175 TOKEN nextToken = NextToken; 176 177 switch (nextToken) { 178 case TOKEN.NONE: 179 return null; 180 case TOKEN.COMMA: 181 continue; 182 case TOKEN.SQUARED_CLOSE: 183 parsing = false; 184 break; 185 default: 186 object value = ParseByToken(nextToken); 187 188 array.Add(value); 189 break; 190 } 191 } 192 193 return array; 194 } 195 196 object ParseValue() { 197 TOKEN nextToken = NextToken; 198 return ParseByToken(nextToken); 199 } 200 201 object ParseByToken(TOKEN token) { 202 switch (token) { 203 case TOKEN.STRING: 204 return ParseString(); 205 case TOKEN.NUMBER: 206 return ParseNumber(); 207 case TOKEN.CURLY_OPEN: 208 return ParseObject(); 209 case TOKEN.SQUARED_OPEN: 210 return ParseArray(); 211 case TOKEN.TRUE: 212 return true; 213 case TOKEN.FALSE: 214 return false; 215 case TOKEN.NULL: 216 return null; 217 default: 218 return null; 219 } 220 } 221 222 string ParseString() { 223 StringBuilder s = new StringBuilder(); 224 char c; 225 226 // ditch opening quote 227 json.Read(); 228 229 bool parsing = true; 230 while (parsing) { 231 232 if (json.Peek() == -1) { 233 parsing = false; 234 break; 235 } 236 237 c = NextChar; 238 switch (c) { 239 case '"': 240 parsing = false; 241 break; 242 case '\\': 243 if (json.Peek() == -1) { 244 parsing = false; 245 break; 246 } 247 248 c = NextChar; 249 switch (c) { 250 case '"': 251 case '\\': 252 case '/': 253 s.Append(c); 254 break; 255 case 'b': 256 s.Append('\b'); 257 break; 258 case 'f': 259 s.Append('\f'); 260 break; 261 case 'n': 262 s.Append('\n'); 263 break; 264 case 'r': 265 s.Append('\r'); 266 break; 267 case 't': 268 s.Append('\t'); 269 break; 270 case 'u': 271 var hex = new char[4]; 272 273 for (int i=0; i< 4; i++) { 274 hex[i] = NextChar; 275 } 276 277 s.Append((char) Convert.ToInt32(new string(hex), 16)); 278 break; 279 } 280 break; 281 default: 282 s.Append(c); 283 break; 284 } 285 } 286 287 return s.ToString(); 288 } 289 290 object ParseNumber() { 291 string number = NextWord; 292 293 if (number.IndexOf('.') == -1) { 294 long parsedInt; 295 Int64.TryParse(number, out parsedInt); 296 return parsedInt; 297 } 298 299 double parsedDouble; 300 Double.TryParse(number, out parsedDouble); 301 return parsedDouble; 302 } 303 304 void EatWhitespace() { 305 while (Char.IsWhiteSpace(PeekChar)) { 306 json.Read(); 307 308 if (json.Peek() == -1) { 309 break; 310 } 311 } 312 } 313 314 char PeekChar { 315 get { 316 return Convert.ToChar(json.Peek()); 317 } 318 } 319 320 char NextChar { 321 get { 322 return Convert.ToChar(json.Read()); 323 } 324 } 325 326 string NextWord { 327 get { 328 StringBuilder word = new StringBuilder(); 329 330 while (!IsWordBreak(PeekChar)) { 331 word.Append(NextChar); 332 333 if (json.Peek() == -1) { 334 break; 335 } 336 } 337 338 return word.ToString(); 339 } 340 } 341 342 TOKEN NextToken { 343 get { 344 EatWhitespace(); 345 346 if (json.Peek() == -1) { 347 return TOKEN.NONE; 348 } 349 350 switch (PeekChar) { 351 case '{': 352 return TOKEN.CURLY_OPEN; 353 case '}': 354 json.Read(); 355 return TOKEN.CURLY_CLOSE; 356 case '[': 357 return TOKEN.SQUARED_OPEN; 358 case ']': 359 json.Read(); 360 return TOKEN.SQUARED_CLOSE; 361 case ',': 362 json.Read(); 363 return TOKEN.COMMA; 364 case '"': 365 return TOKEN.STRING; 366 case ':': 367 return TOKEN.COLON; 368 case '0': 369 case '1': 370 case '2': 371 case '3': 372 case '4': 373 case '5': 374 case '6': 375 case '7': 376 case '8': 377 case '9': 378 case '-': 379 return TOKEN.NUMBER; 380 } 381 382 switch (NextWord) { 383 case "false": 384 return TOKEN.FALSE; 385 case "true": 386 return TOKEN.TRUE; 387 case "null": 388 return TOKEN.NULL; 389 } 390 391 return TOKEN.NONE; 392 } 393 } 394 } 395 396 /// <summary> 397 /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string 398 /// </summary> 399 /// <param name="json">A Dictionary<string, object> / List<object></param> 400 /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> 401 public static string Serialize(object obj) { 402 return Serializer.Serialize(obj); 403 } 404 405 sealed class Serializer { 406 StringBuilder builder; 407 408 Serializer() { 409 builder = new StringBuilder(); 410 } 411 412 public static string Serialize(object obj) { 413 var instance = new Serializer(); 414 415 instance.SerializeValue(obj); 416 417 return instance.builder.ToString(); 418 } 419 420 void SerializeValue(object value) { 421 IList asList; 422 IDictionary asDict; 423 string asStr; 424 425 if (value == null) { 426 builder.Append("null"); 427 } else if ((asStr = value as string) != null) { 428 SerializeString(asStr); 429 } else if (value is bool) { 430 builder.Append((bool) value ? "true" : "false"); 431 } else if ((asList = value as IList) != null) { 432 SerializeArray(asList); 433 } else if ((asDict = value as IDictionary) != null) { 434 SerializeObject(asDict); 435 } else if (value is char) { 436 SerializeString(new string((char) value, 1)); 437 } else { 438 SerializeOther(value); 439 } 440 } 441 442 void SerializeObject(IDictionary obj) { 443 bool first = true; 444 445 builder.Append('{'); 446 447 foreach (object e in obj.Keys) { 448 if (!first) { 449 builder.Append(','); 450 } 451 452 SerializeString(e.ToString()); 453 builder.Append(':'); 454 455 SerializeValue(obj[e]); 456 457 first = false; 458 } 459 460 builder.Append('}'); 461 } 462 463 void SerializeArray(IList anArray) { 464 builder.Append('['); 465 466 bool first = true; 467 468 foreach (object obj in anArray) { 469 if (!first) { 470 builder.Append(','); 471 } 472 473 SerializeValue(obj); 474 475 first = false; 476 } 477 478 builder.Append(']'); 479 } 480 481 void SerializeString(string str) { 482 builder.Append('\"'); 483 484 char[] charArray = str.ToCharArray(); 485 foreach (var c in charArray) { 486 switch (c) { 487 case '"': 488 builder.Append("\\\""); 489 break; 490 case '\\': 491 builder.Append("\\\\"); 492 break; 493 case '\b': 494 builder.Append("\\b"); 495 break; 496 case '\f': 497 builder.Append("\\f"); 498 break; 499 case '\n': 500 builder.Append("\\n"); 501 break; 502 case '\r': 503 builder.Append("\\r"); 504 break; 505 case '\t': 506 builder.Append("\\t"); 507 break; 508 default: 509 int codepoint = Convert.ToInt32(c); 510 if ((codepoint >= 32) && (codepoint <= 126)) { 511 builder.Append(c); 512 } else { 513 builder.Append("\\u"); 514 builder.Append(codepoint.ToString("x4")); 515 } 516 break; 517 } 518 } 519 520 builder.Append('\"'); 521 } 522 523 void SerializeOther(object value) { 524 // NOTE: decimals lose precision during serialization. 525 // They always have, I'm just letting you know. 526 // Previously floats and doubles lost precision too. 527 if (value is float) { 528 builder.Append(((float) value).ToString("R")); 529 } else if (value is int 530 || value is uint 531 || value is long 532 || value is sbyte 533 || value is byte 534 || value is short 535 || value is ushort 536 || value is ulong) { 537 builder.Append(value); 538 } else if (value is double 539 || value is decimal) { 540 builder.Append(Convert.ToDouble(value).ToString("R")); 541 } else { 542 SerializeString(value.ToString()); 543 } 544 } 545 } 546 } 547 }