631 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			631 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | /* | ||
|  |  * MIT License.  Forked from GA_MiniJSON. | ||
|  |  * I modified it so that it could be used for TD limitations. | ||
|  |  */ | ||
|  | // using UnityEngine; | ||
|  | using System; | ||
|  | using System.Collections; | ||
|  | using System.Collections.Generic; | ||
|  | using System.IO; | ||
|  | using System.Text; | ||
|  | using System.Globalization; | ||
|  | 
 | ||
|  | namespace ThinkingSDK.PC.Utils | ||
|  | { | ||
|  | 	/* Based on the JSON parser from  | ||
|  | 	 * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html | ||
|  | 	 *  | ||
|  | 	 * I simplified it so that it doesn't throw exceptions | ||
|  | 	 * and can be used in Unity iPhone with maximum code stripping. | ||
|  | 	 */ | ||
|  | 	/// <summary> | ||
|  | 	/// This class encodes and decodes JSON strings. | ||
|  | 	/// Spec. details, see http://www.json.org/ | ||
|  | 	///  | ||
|  | 	/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable. | ||
|  | 	/// All numbers are parsed to floats. | ||
|  | 	/// </summary> | ||
|  | 	public class ThinkingSDKJSON | ||
|  | 	{ | ||
|  |         /// <summary> | ||
|  |         /// Parses the string json into a value | ||
|  |         /// </summary> | ||
|  |         /// <param name="json">A JSON string.</param> | ||
|  |         /// <returns>An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false</returns> | ||
|  |         public static Dictionary<string, object> Deserialize(string json) | ||
|  |         { | ||
|  |             // save the string for debug information | ||
|  |             if (json == null) | ||
|  |             { | ||
|  |                 return null; | ||
|  |             } | ||
|  | 
 | ||
|  |             return Parser.Parse(json); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Use caution! | ||
|  |         public static List<object> DeserializeArray(string json) | ||
|  |         { | ||
|  |             // save the string for debug information | ||
|  |             if (json == null) | ||
|  |             { | ||
|  |                 return null; | ||
|  |             } | ||
|  | 
 | ||
|  |             return Parser.ParseArray(json); | ||
|  |         } | ||
|  | 
 | ||
|  |         sealed class Parser : IDisposable | ||
|  |         { | ||
|  |             const string WORD_BREAK = "{}[],:\""; | ||
|  | 
 | ||
|  |             public static bool IsWordBreak(char c) | ||
|  |             { | ||
|  |                 return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1; | ||
|  |             } | ||
|  | 
 | ||
|  |             enum TOKEN | ||
|  |             { | ||
|  |                 NONE, | ||
|  |                 CURLY_OPEN, | ||
|  |                 CURLY_CLOSE, | ||
|  |                 SQUARED_OPEN, | ||
|  |                 SQUARED_CLOSE, | ||
|  |                 COLON, | ||
|  |                 COMMA, | ||
|  |                 STRING, | ||
|  |                 NUMBER, | ||
|  |                 TRUE, | ||
|  |                 FALSE, | ||
|  |                 NULL | ||
|  |             }; | ||
|  | 
 | ||
|  |             StringReader json; | ||
|  | 
 | ||
|  |             Parser(string jsonString) | ||
|  |             { | ||
|  |                 json = new StringReader(jsonString); | ||
|  |             } | ||
|  | 
 | ||
|  |             public static Dictionary<string, object> Parse(string jsonString) | ||
|  |             { | ||
|  |                 using (var instance = new Parser(jsonString)) | ||
|  |                 { | ||
|  |                     return instance.ParseObject(); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public static List<object> ParseArray(string jsonString) | ||
|  |             { | ||
|  |                 using (var instance = new Parser(jsonString)) | ||
|  |                 { | ||
|  |                     return instance.ParseArray(); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Dispose() | ||
|  |             { | ||
|  |                 json.Dispose(); | ||
|  |                 json = null; | ||
|  |             } | ||
|  | 
 | ||
|  |             Dictionary<string, object> ParseObject() | ||
|  |             { | ||
|  |                 Dictionary<string, object> table = new Dictionary<string, object>(); | ||
|  | 
 | ||
|  |                 // ditch opening brace | ||
|  |                 json.Read(); | ||
|  | 
 | ||
|  |                 // { | ||
|  |                 while (true) | ||
|  |                 { | ||
|  |                     switch (NextToken) | ||
|  |                     { | ||
|  |                         case TOKEN.NONE: | ||
|  |                             return null; | ||
|  |                         case TOKEN.COMMA: | ||
|  |                             continue; | ||
|  |                         case TOKEN.CURLY_CLOSE: | ||
|  |                             return table; | ||
|  |                         default: | ||
|  |                             // name | ||
|  |                             string name = ParseString(); | ||
|  |                             if (name == null) | ||
|  |                             { | ||
|  |                                 return null; | ||
|  |                             } | ||
|  | 
 | ||
|  |                             // : | ||
|  |                             if (NextToken != TOKEN.COLON) | ||
|  |                             { | ||
|  |                                 return null; | ||
|  |                             } | ||
|  |                             // ditch the colon | ||
|  |                             json.Read(); | ||
|  | 
 | ||
|  |                             // value | ||
|  |                             table[name] = ParseValue(); | ||
|  |                             break; | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             List<object> ParseArray() | ||
|  |             { | ||
|  |                 List<object> array = new List<object>(); | ||
|  | 
 | ||
|  |                 // ditch opening bracket | ||
|  |                 json.Read(); | ||
|  | 
 | ||
|  |                 // [ | ||
|  |                 var parsing = true; | ||
|  |                 while (parsing) | ||
|  |                 { | ||
|  |                     TOKEN nextToken = NextToken; | ||
|  | 
 | ||
|  |                     switch (nextToken) | ||
|  |                     { | ||
|  |                         case TOKEN.NONE: | ||
|  |                             return null; | ||
|  |                         case TOKEN.COMMA: | ||
|  |                             continue; | ||
|  |                         case TOKEN.SQUARED_CLOSE: | ||
|  |                             parsing = false; | ||
|  |                             break; | ||
|  |                         default: | ||
|  |                             object value = ParseByToken(nextToken); | ||
|  | 
 | ||
|  |                             array.Add(value); | ||
|  |                             break; | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return array; | ||
|  |             } | ||
|  | 
 | ||
|  |             object ParseValue() | ||
|  |             { | ||
|  |                 TOKEN nextToken = NextToken; | ||
|  |                 return ParseByToken(nextToken); | ||
|  |             } | ||
|  | 
 | ||
|  |             object ParseByToken(TOKEN token) | ||
|  |             { | ||
|  |                 switch (token) | ||
|  |                 { | ||
|  |                     case TOKEN.STRING: | ||
|  |                         string str = ParseString(); | ||
|  |                         DateTime dateTime; | ||
|  |                         if (DateTime.TryParseExact(str, "yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) | ||
|  |                         { | ||
|  |                             return dateTime; | ||
|  |                         } | ||
|  |                         return str; | ||
|  |                     case TOKEN.NUMBER: | ||
|  |                         return ParseNumber(); | ||
|  |                     case TOKEN.CURLY_OPEN: | ||
|  |                         return ParseObject(); | ||
|  |                     case TOKEN.SQUARED_OPEN: | ||
|  |                         return ParseArray(); | ||
|  |                     case TOKEN.TRUE: | ||
|  |                         return true; | ||
|  |                     case TOKEN.FALSE: | ||
|  |                         return false; | ||
|  |                     case TOKEN.NULL: | ||
|  |                         return null; | ||
|  |                     default: | ||
|  |                         return null; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             string ParseString() | ||
|  |             { | ||
|  |                 StringBuilder s = new StringBuilder(); | ||
|  |                 char c; | ||
|  | 
 | ||
|  |                 // ditch opening quote | ||
|  |                 json.Read(); | ||
|  | 
 | ||
|  |                 bool parsing = true; | ||
|  |                 while (parsing) | ||
|  |                 { | ||
|  | 
 | ||
|  |                     if (json.Peek() == -1) | ||
|  |                     { | ||
|  |                         parsing = false; | ||
|  |                         break; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     c = NextChar; | ||
|  |                     switch (c) | ||
|  |                     { | ||
|  |                         case '"': | ||
|  |                             parsing = false; | ||
|  |                             break; | ||
|  |                         case '\\': | ||
|  |                             if (json.Peek() == -1) | ||
|  |                             { | ||
|  |                                 parsing = false; | ||
|  |                                 break; | ||
|  |                             } | ||
|  | 
 | ||
|  |                             c = NextChar; | ||
|  |                             switch (c) | ||
|  |                             { | ||
|  |                                 case '"': | ||
|  |                                 case '\\': | ||
|  |                                 case '/': | ||
|  |                                     s.Append(c); | ||
|  |                                     break; | ||
|  |                                 case 'b': | ||
|  |                                     s.Append('\b'); | ||
|  |                                     break; | ||
|  |                                 case 'f': | ||
|  |                                     s.Append('\f'); | ||
|  |                                     break; | ||
|  |                                 case 'n': | ||
|  |                                     s.Append('\n'); | ||
|  |                                     break; | ||
|  |                                 case 'r': | ||
|  |                                     s.Append('\r'); | ||
|  |                                     break; | ||
|  |                                 case 't': | ||
|  |                                     s.Append('\t'); | ||
|  |                                     break; | ||
|  |                                 case 'u': | ||
|  |                                     var hex = new char[4]; | ||
|  | 
 | ||
|  |                                     for (int i = 0; i < 4; i++) | ||
|  |                                     { | ||
|  |                                         hex[i] = NextChar; | ||
|  |                                     } | ||
|  | 
 | ||
|  |                                     s.Append((char)Convert.ToInt32(new string(hex), 16)); | ||
|  |                                     break; | ||
|  |                             } | ||
|  |                             break; | ||
|  |                         default: | ||
|  |                             s.Append(c); | ||
|  |                             break; | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return s.ToString(); | ||
|  |             } | ||
|  | 
 | ||
|  |             object ParseNumber() | ||
|  |             { | ||
|  |                 string number = NextWord; | ||
|  | 
 | ||
|  |                 if (number.IndexOf('.') == -1) | ||
|  |                 { | ||
|  |                     long parsedInt; | ||
|  |                     Int64.TryParse(number, out parsedInt); | ||
|  |                     return parsedInt; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 double parsedDouble; | ||
|  |                 if (!Double.TryParse(number, System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble)) | ||
|  |                 { | ||
|  |                     Double.TryParse(number, System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign, System.Globalization.CultureInfo.CreateSpecificCulture("es-ES"), out parsedDouble); | ||
|  |                 } | ||
|  |                 return parsedDouble; | ||
|  |             } | ||
|  | 
 | ||
|  |             void EatWhitespace() | ||
|  |             { | ||
|  |                 while (Char.IsWhiteSpace(PeekChar)) | ||
|  |                 { | ||
|  |                     json.Read(); | ||
|  | 
 | ||
|  |                     if (json.Peek() == -1) | ||
|  |                     { | ||
|  |                         break; | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             char PeekChar | ||
|  |             { | ||
|  |                 get | ||
|  |                 { | ||
|  |                     return Convert.ToChar(json.Peek()); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             char NextChar | ||
|  |             { | ||
|  |                 get | ||
|  |                 { | ||
|  |                     return Convert.ToChar(json.Read()); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             string NextWord | ||
|  |             { | ||
|  |                 get | ||
|  |                 { | ||
|  |                     StringBuilder word = new StringBuilder(); | ||
|  | 
 | ||
|  |                     while (!IsWordBreak(PeekChar)) | ||
|  |                     { | ||
|  |                         word.Append(NextChar); | ||
|  | 
 | ||
|  |                         if (json.Peek() == -1) | ||
|  |                         { | ||
|  |                             break; | ||
|  |                         } | ||
|  |                     } | ||
|  | 
 | ||
|  |                     return word.ToString(); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             TOKEN NextToken | ||
|  |             { | ||
|  |                 get | ||
|  |                 { | ||
|  |                     EatWhitespace(); | ||
|  | 
 | ||
|  |                     if (json.Peek() == -1) | ||
|  |                     { | ||
|  |                         return TOKEN.NONE; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     switch (PeekChar) | ||
|  |                     { | ||
|  |                         case '{': | ||
|  |                             return TOKEN.CURLY_OPEN; | ||
|  |                         case '}': | ||
|  |                             json.Read(); | ||
|  |                             return TOKEN.CURLY_CLOSE; | ||
|  |                         case '[': | ||
|  |                             return TOKEN.SQUARED_OPEN; | ||
|  |                         case ']': | ||
|  |                             json.Read(); | ||
|  |                             return TOKEN.SQUARED_CLOSE; | ||
|  |                         case ',': | ||
|  |                             json.Read(); | ||
|  |                             return TOKEN.COMMA; | ||
|  |                         case '"': | ||
|  |                             return TOKEN.STRING; | ||
|  |                         case ':': | ||
|  |                             return TOKEN.COLON; | ||
|  |                         case '0': | ||
|  |                         case '1': | ||
|  |                         case '2': | ||
|  |                         case '3': | ||
|  |                         case '4': | ||
|  |                         case '5': | ||
|  |                         case '6': | ||
|  |                         case '7': | ||
|  |                         case '8': | ||
|  |                         case '9': | ||
|  |                         case '-': | ||
|  |                             return TOKEN.NUMBER; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     switch (NextWord) | ||
|  |                     { | ||
|  |                         case "false": | ||
|  |                             return TOKEN.FALSE; | ||
|  |                         case "true": | ||
|  |                             return TOKEN.TRUE; | ||
|  |                         case "null": | ||
|  |                             return TOKEN.NULL; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     return TOKEN.NONE; | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string | ||
|  |         /// </summary> | ||
|  |         /// <param name="json">A Dictionary<string, object> / List<object></param> | ||
|  |         /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns> | ||
|  |         public static string Serialize(object obj, Func<DateTime, string> func = null) | ||
|  |         { | ||
|  |             return Serializer.Serialize(obj, func); | ||
|  |         } | ||
|  | 
 | ||
|  |         sealed class Serializer | ||
|  |         { | ||
|  |             StringBuilder builder; | ||
|  |             Func<DateTime, string> func; | ||
|  | 
 | ||
|  |             Serializer() | ||
|  |             { | ||
|  |                 builder = new StringBuilder(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public static string Serialize(object obj, Func<DateTime, string> func) | ||
|  |             { | ||
|  |                 var instance = new Serializer(); | ||
|  |                 instance.func = func; | ||
|  | 
 | ||
|  |                 instance.SerializeValue(obj); | ||
|  | 
 | ||
|  |                 return instance.builder.ToString(); | ||
|  |             } | ||
|  | 
 | ||
|  |             void SerializeValue(object value) | ||
|  |             { | ||
|  |                 IList asList; | ||
|  |                 IDictionary asDict; | ||
|  |                 string asStr; | ||
|  | 
 | ||
|  |                 if (value == null) | ||
|  |                 { | ||
|  |                     builder.Append("null"); | ||
|  |                 } | ||
|  |                 else if ((asStr = value as string) != null) | ||
|  |                 { | ||
|  |                     SerializeString(asStr); | ||
|  |                 } | ||
|  |                 else if (value is bool) | ||
|  |                 { | ||
|  |                     builder.Append((bool)value ? "true" : "false"); | ||
|  |                 } | ||
|  |                 else if ((asList = value as IList) != null) | ||
|  |                 { | ||
|  |                     SerializeArray(asList); | ||
|  |                 } | ||
|  |                 else if ((asDict = value as IDictionary) != null) | ||
|  |                 { | ||
|  |                     SerializeObject(asDict); | ||
|  |                 } | ||
|  |                 else if (value is char) | ||
|  |                 { | ||
|  |                     SerializeString(new string((char)value, 1)); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     SerializeOther(value); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             void SerializeObject(IDictionary obj) | ||
|  |             { | ||
|  |                 bool first = true; | ||
|  | 
 | ||
|  |                 builder.Append('{'); | ||
|  | 
 | ||
|  |                 foreach (object e in obj.Keys) | ||
|  |                 { | ||
|  |                     if (!first) | ||
|  |                     { | ||
|  |                         builder.Append(','); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     SerializeString(e.ToString()); | ||
|  |                     builder.Append(':'); | ||
|  | 
 | ||
|  |                     SerializeValue(obj[e]); | ||
|  | 
 | ||
|  |                     first = false; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 builder.Append('}'); | ||
|  |             } | ||
|  | 
 | ||
|  |             void SerializeArray(IList anArray) | ||
|  |             { | ||
|  |                 builder.Append('['); | ||
|  | 
 | ||
|  |                 bool first = true; | ||
|  | 
 | ||
|  |                 foreach (object obj in anArray) | ||
|  |                 { | ||
|  |                     if (!first) | ||
|  |                     { | ||
|  |                         builder.Append(','); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     SerializeValue(obj); | ||
|  | 
 | ||
|  |                     first = false; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 builder.Append(']'); | ||
|  |             } | ||
|  | 
 | ||
|  |             void SerializeString(string str) | ||
|  |             { | ||
|  |                 builder.Append('\"'); | ||
|  | 
 | ||
|  |                 char[] charArray = str.ToCharArray(); | ||
|  |                 foreach (var c in charArray) | ||
|  |                 { | ||
|  |                     switch (c) | ||
|  |                     { | ||
|  |                         case '"': | ||
|  |                             builder.Append("\\\""); | ||
|  |                             break; | ||
|  |                         case '\\': | ||
|  |                             builder.Append("\\\\"); | ||
|  |                             break; | ||
|  |                         case '\b': | ||
|  |                             builder.Append("\\b"); | ||
|  |                             break; | ||
|  |                         case '\f': | ||
|  |                             builder.Append("\\f"); | ||
|  |                             break; | ||
|  |                         case '\n': | ||
|  |                             builder.Append("\\n"); | ||
|  |                             break; | ||
|  |                         case '\r': | ||
|  |                             builder.Append("\\r"); | ||
|  |                             break; | ||
|  |                         case '\t': | ||
|  |                             builder.Append("\\t"); | ||
|  |                             break; | ||
|  |                         default: | ||
|  |                             int codepoint = Convert.ToInt32(c); | ||
|  |                             if ((codepoint >= 32) && (codepoint <= 126)) | ||
|  |                             { | ||
|  |                                 builder.Append(c); | ||
|  |                             } | ||
|  |                             else | ||
|  |                             { | ||
|  |                                 builder.Append("\\u"); | ||
|  |                                 builder.Append(codepoint.ToString("x4")); | ||
|  |                             } | ||
|  |                             break; | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 builder.Append('\"'); | ||
|  |             } | ||
|  | 
 | ||
|  |             void SerializeOther(object value) | ||
|  |             { | ||
|  |                 // NOTE: decimals lose precision during serialization. | ||
|  |                 // They always have, I'm just letting you know. | ||
|  |                 // Previously floats and doubles lost precision too. | ||
|  |                 if (value is float) | ||
|  |                 { | ||
|  |                     builder.Append(((float)value).ToString("R", System.Globalization.CultureInfo.InvariantCulture)); | ||
|  |                 } | ||
|  |                 else if (value is int | ||
|  |                   || value is uint | ||
|  |                   || value is long | ||
|  |                   || value is sbyte | ||
|  |                   || value is byte | ||
|  |                   || value is short | ||
|  |                   || value is ushort | ||
|  |                   || value is ulong) | ||
|  |                 { | ||
|  |                     builder.Append(value); | ||
|  |                 } | ||
|  |                 else if (value is double) | ||
|  |                 { | ||
|  |                     builder.Append(Convert.ToDouble(value).ToString("R", System.Globalization.CultureInfo.InvariantCulture)); | ||
|  |                 } | ||
|  |                 else if (value is decimal) { | ||
|  |                     builder.Append(Convert.ToDecimal(value).ToString("R", System.Globalization.CultureInfo.InvariantCulture)); | ||
|  |                 } | ||
|  |                 else if (value is DateTime) | ||
|  |                 { | ||
|  |                     builder.Append('\"'); | ||
|  |                     DateTime dateTime = (DateTime)value; | ||
|  |                     if (null != func) | ||
|  |                     { | ||
|  |                         builder.Append(func((DateTime)value)); | ||
|  |                     } | ||
|  |                     else | ||
|  |                     { | ||
|  |                         builder.Append(dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture)); | ||
|  |                     } | ||
|  |                     builder.Append('\"'); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     SerializeString(value.ToString()); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |