| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741 | using System;using System.Collections;using System.Text;using System.Collections.Generic;/* 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 doubles./// </summary>public class MiniJSON{	private const int TOKEN_NONE = 0;	private const int TOKEN_CURLY_OPEN = 1;	private const int TOKEN_CURLY_CLOSE = 2;	private const int TOKEN_SQUARED_OPEN = 3;	private const int TOKEN_SQUARED_CLOSE = 4;	private const int TOKEN_COLON = 5;	private const int TOKEN_COMMA = 6;	private const int TOKEN_STRING = 7;	private const int TOKEN_NUMBER = 8;	private const int TOKEN_TRUE = 9;	private const int TOKEN_FALSE = 10;	private const int TOKEN_NULL = 11;	private const int BUILDER_CAPACITY = 2000;	/// <summary>	/// On decoding, this value holds the position at which the parse failed (-1 = no error).	/// </summary>	protected static int lastErrorIndex = -1;	protected static string lastDecode = "";	/// <summary>	/// Parses the string json into a value	/// </summary>	/// <param name="json">A JSON string.</param>	/// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>	public static object jsonDecode( string json )	{		// save the string for debug information		MiniJSON.lastDecode = json;		if( json != null )		{			char[] charArray = json.ToCharArray();			int index = 0;			bool success = true;			object value = MiniJSON.parseValue( charArray, ref index, ref success );			if( success )				MiniJSON.lastErrorIndex = -1;			else				MiniJSON.lastErrorIndex = index;			return value;		}		else		{			return null;		}	}	/// <summary>	/// Converts a Hashtable / ArrayList / Dictionary(string,string) object into a JSON string	/// </summary>	/// <param name="json">A Hashtable / ArrayList</param>	/// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>	public static string jsonEncode( object json )	{		var builder = new StringBuilder( BUILDER_CAPACITY );		var success = MiniJSON.serializeValue( json, builder );		return ( success ? builder.ToString() : null );	}	/// <summary>	/// On decoding, this function returns the position at which the parse failed (-1 = no error).	/// </summary>	/// <returns></returns>	public static bool lastDecodeSuccessful()	{		return ( MiniJSON.lastErrorIndex == -1 );	}	/// <summary>	/// On decoding, this function returns the position at which the parse failed (-1 = no error).	/// </summary>	/// <returns></returns>	public static int getLastErrorIndex()	{		return MiniJSON.lastErrorIndex;	}	/// <summary>	/// If a decoding error occurred, this function returns a piece of the JSON string 	/// at which the error took place. To ease debugging.	/// </summary>	/// <returns></returns>	public static string getLastErrorSnippet()	{		if( MiniJSON.lastErrorIndex == -1 )		{			return "";		}		else		{			int startIndex = MiniJSON.lastErrorIndex - 5;			int endIndex = MiniJSON.lastErrorIndex + 15;			if( startIndex < 0 )				startIndex = 0;			if( endIndex >= MiniJSON.lastDecode.Length )				endIndex = MiniJSON.lastDecode.Length - 1;			return MiniJSON.lastDecode.Substring( startIndex, endIndex - startIndex + 1 );		}	}	#region Parsing	protected static Hashtable parseObject( char[] json, ref int index )	{		Hashtable table = new Hashtable();		int token;		// {		nextToken( json, ref index );		bool done = false;		while( !done )		{			token = lookAhead( json, index );			if( token == MiniJSON.TOKEN_NONE )			{				return null;			}			else if( token == MiniJSON.TOKEN_COMMA )			{				nextToken( json, ref index );			}			else if( token == MiniJSON.TOKEN_CURLY_CLOSE )			{				nextToken( json, ref index );				return table;			}			else			{				// name				string name = parseString( json, ref index );				if( name == null )				{					return null;				}				// :				token = nextToken( json, ref index );				if( token != MiniJSON.TOKEN_COLON )					return null;				// value				bool success = true;				object value = parseValue( json, ref index, ref success );				if( !success )					return null;				table[name] = value;			}		}		return table;	}	protected static ArrayList parseArray( char[] json, ref int index )	{		ArrayList array = new ArrayList();		// [		nextToken( json, ref index );		bool done = false;		while( !done )		{			int token = lookAhead( json, index );			if( token == MiniJSON.TOKEN_NONE )			{				return null;			}			else if( token == MiniJSON.TOKEN_COMMA )			{				nextToken( json, ref index );			}			else if( token == MiniJSON.TOKEN_SQUARED_CLOSE )			{				nextToken( json, ref index );				break;			}			else			{				bool success = true;				object value = parseValue( json, ref index, ref success );				if( !success )					return null;				array.Add( value );			}		}		return array;	}	protected static object parseValue( char[] json, ref int index, ref bool success )	{		switch( lookAhead( json, index ) )		{		case MiniJSON.TOKEN_STRING:			return parseString( json, ref index );		case MiniJSON.TOKEN_NUMBER:			return parseNumber( json, ref index );		case MiniJSON.TOKEN_CURLY_OPEN:			return parseObject( json, ref index );		case MiniJSON.TOKEN_SQUARED_OPEN:			return parseArray( json, ref index );		case MiniJSON.TOKEN_TRUE:			nextToken( json, ref index );			return Boolean.Parse( "TRUE" );		case MiniJSON.TOKEN_FALSE:			nextToken( json, ref index );			return Boolean.Parse( "FALSE" );		case MiniJSON.TOKEN_NULL:			nextToken( json, ref index );			return null;		case MiniJSON.TOKEN_NONE:			break;		}		success = false;		return null;	}	protected static string parseString( char[] json, ref int index )	{		string s = "";		char c;		eatWhitespace( json, ref index );		// "		c = json[index++];		bool complete = false;		while( !complete )		{			if( index == json.Length )				break;			c = json[index++];			if( c == '"' )			{				complete = true;				break;			}			else if( c == '\\' )			{				if( index == json.Length )					break;				c = json[index++];				if( c == '"' )				{					s += '"';				}				else if( c == '\\' )				{					s += '\\';				}				else if( c == '/' )				{					s += '/';				}				else if( c == 'b' )				{					s += '\b';				}				else if( c == 'f' )				{					s += '\f';				}				else if( c == 'n' )				{					s += '\n';				}				else if( c == 'r' )				{					s += '\r';				}				else if( c == 't' )				{					s += '\t';				}				else if( c == 'u' )				{					int remainingLength = json.Length - index;					if( remainingLength >= 4 )					{						char[] unicodeCharArray = new char[4];						Array.Copy( json, index, unicodeCharArray, 0, 4 );						uint codePoint = UInt32.Parse( new string( unicodeCharArray ), System.Globalization.NumberStyles.HexNumber );						// convert the integer codepoint to a unicode char and add to string						s += Char.ConvertFromUtf32( (int)codePoint );						// skip 4 chars						index += 4;					}					else					{						break;					}				}			}			else			{				s += c;			}		}		if( !complete )			return null;		return s;	}	protected static double parseNumber( char[] json, ref int index )	{		eatWhitespace( json, ref index );		int lastIndex = getLastIndexOfNumber( json, index );		int charLength = ( lastIndex - index ) + 1;		char[] numberCharArray = new char[charLength];		Array.Copy( json, index, numberCharArray, 0, charLength );		index = lastIndex + 1;		return Double.Parse( new string( numberCharArray ) ); // , CultureInfo.InvariantCulture);	}	protected static int getLastIndexOfNumber( char[] json, int index )	{		int lastIndex;		for( lastIndex = index; lastIndex < json.Length; lastIndex++ )			if( "0123456789+-.eE".IndexOf( json[lastIndex] ) == -1 )			{				break;			}		return lastIndex - 1;	}	protected static void eatWhitespace( char[] json, ref int index )	{		for( ; index < json.Length; index++ )			if( " \t\n\r".IndexOf( json[index] ) == -1 )			{				break;			}	}	protected static int lookAhead( char[] json, int index )	{		int saveIndex = index;		return nextToken( json, ref saveIndex );	}	protected static int nextToken( char[] json, ref int index )	{		eatWhitespace( json, ref index );		if( index == json.Length )		{			return MiniJSON.TOKEN_NONE;		}		char c = json[index];		index++;		switch( c )		{		case '{':			return MiniJSON.TOKEN_CURLY_OPEN;		case '}':			return MiniJSON.TOKEN_CURLY_CLOSE;		case '[':			return MiniJSON.TOKEN_SQUARED_OPEN;		case ']':			return MiniJSON.TOKEN_SQUARED_CLOSE;		case ',':			return MiniJSON.TOKEN_COMMA;		case '"':			return MiniJSON.TOKEN_STRING;		case '0':		case '1':		case '2':		case '3':		case '4': 		case '5':		case '6':		case '7':		case '8':		case '9':		case '-': 			return MiniJSON.TOKEN_NUMBER;		case ':':			return MiniJSON.TOKEN_COLON;		}		index--;		int remainingLength = json.Length - index;		// false		if( remainingLength >= 5 )		{			if( json[index] == 'f' &&				json[index + 1] == 'a' &&				json[index + 2] == 'l' &&				json[index + 3] == 's' &&				json[index + 4] == 'e' )			{				index += 5;				return MiniJSON.TOKEN_FALSE;			}		}		// true		if( remainingLength >= 4 )		{			if( json[index] == 't' &&				json[index + 1] == 'r' &&				json[index + 2] == 'u' &&				json[index + 3] == 'e' )			{				index += 4;				return MiniJSON.TOKEN_TRUE;			}		}		// null		if( remainingLength >= 4 )		{			if( json[index] == 'n' &&				json[index + 1] == 'u' &&				json[index + 2] == 'l' &&				json[index + 3] == 'l' )			{				index += 4;				return MiniJSON.TOKEN_NULL;			}		}		return MiniJSON.TOKEN_NONE;	}	#endregion	#region Serialization	protected static bool serializeObjectOrArray( object objectOrArray, StringBuilder builder )	{		if( objectOrArray is Hashtable )		{			return serializeObject( (Hashtable)objectOrArray, builder );		}		else if( objectOrArray is ArrayList )		{			return serializeArray( (ArrayList)objectOrArray, builder );		}		else		{			return false;		}	}	protected static bool serializeObject( Hashtable anObject, StringBuilder builder )	{		builder.Append( "{" );		IDictionaryEnumerator e = anObject.GetEnumerator();		bool first = true;		while( e.MoveNext() )		{			string key = e.Key.ToString();			object value = e.Value;			if( !first )			{				builder.Append( ", " );			}			serializeString( key, builder );			builder.Append( ":" );			if( !serializeValue( value, builder ) )			{				return false;			}			first = false;		}		builder.Append( "}" );		return true;	}	protected static bool serializeDictionary( Dictionary<string,string> dict, StringBuilder builder )	{		builder.Append( "{" );		bool first = true;		foreach( var kv in dict )		{			if( !first )				builder.Append( ", " );			serializeString( kv.Key, builder );			builder.Append( ":" );			serializeString( kv.Value, builder );			first = false;		}		builder.Append( "}" );		return true;	}	protected static bool serializeArray( ArrayList anArray, StringBuilder builder )	{		builder.Append( "[" );		bool first = true;		for( int i = 0; i < anArray.Count; i++ )		{			object value = anArray[i];			if( !first )			{				builder.Append( ", " );			}			if( !serializeValue( value, builder ) )			{				return false;			}			first = false;		}		builder.Append( "]" );		return true;	}	protected static bool serializeValue( object value, StringBuilder builder )	{		//Type t = value.GetType();		//UnityEngine.Debug.Log("type: " + t.ToString() + " isArray: " + t.IsArray);		if( value == null )		{			builder.Append( "null" );		}		else if( value.GetType().IsArray )		{			serializeArray( new ArrayList( (ICollection)value ), builder );		}		else if( value is string )		{			serializeString( (string)value, builder );		}		else if( value is Char )		{			serializeString( Convert.ToString( (char)value ), builder );		}		else if( value is decimal )		{			serializeString( Convert.ToString( (decimal)value ), builder );		}		else if( value is Hashtable )		{			serializeObject( (Hashtable)value, builder );		}		else if( value is Dictionary<string,string> )		{			serializeDictionary( (Dictionary<string,string>)value, builder );		}		else if( value is ArrayList )		{			serializeArray( (ArrayList)value, builder );		}		else if( ( value is Boolean ) && ( (Boolean)value == true ) )		{			builder.Append( "true" );		}		else if( ( value is Boolean ) && ( (Boolean)value == false ) )		{			builder.Append( "false" );		}		else if( value.GetType().IsPrimitive )		{			serializeNumber( Convert.ToDouble( value ), builder );		}		else		{			return false;		}		return true;	}	protected static void serializeString( string aString, StringBuilder builder )	{		builder.Append( "\"" );		char[] charArray = aString.ToCharArray();		for( int i = 0; i < charArray.Length; i++ )		{			char c = charArray[i];			if( c == '"' )			{				builder.Append( "\\\"" );			}			else if( c == '\\' )			{				builder.Append( "\\\\" );			}			else if( c == '\b' )			{				builder.Append( "\\b" );			}			else if( c == '\f' )			{				builder.Append( "\\f" );			}			else if( c == '\n' )			{				builder.Append( "\\n" );			}			else if( c == '\r' )			{				builder.Append( "\\r" );			}			else if( c == '\t' )			{				builder.Append( "\\t" );			}			else			{				int codepoint = Convert.ToInt32( c );				if( ( codepoint >= 32 ) && ( codepoint <= 126 ) )				{					builder.Append( c );				}				else				{					builder.Append( "\\u" + Convert.ToString( codepoint, 16 ).PadLeft( 4, '0' ) );				}			}		}		builder.Append( "\"" );	}	protected static void serializeNumber( double number, StringBuilder builder )	{		builder.Append( Convert.ToString( number ) ); // , CultureInfo.InvariantCulture));	}	#endregion}#region Extension methodspublic static class MiniJsonExtensions{	public static string toJson( this Hashtable obj )	{		return MiniJSON.jsonEncode( obj );	}	public static string toJson( this Dictionary<string,string> obj )	{		return MiniJSON.jsonEncode( obj );	}	public static ArrayList arrayListFromJson( this string json )	{		return MiniJSON.jsonDecode( json ) as ArrayList;	}	public static Hashtable hashtableFromJson( this string json )	{		return MiniJSON.jsonDecode( json ) as Hashtable;	}}#endregion
 |