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 methods
- public 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
|