--- /dev/null
+/*\r
+ Copyright (c) 2008, Adobe Systems Incorporated\r
+ All rights reserved.\r
+\r
+ Redistribution and use in source and binary forms, with or without \r
+ modification, are permitted provided that the following conditions are\r
+ met:\r
+\r
+ * Redistributions of source code must retain the above copyright notice, \r
+ this list of conditions and the following disclaimer.\r
+ \r
+ * Redistributions in binary form must reproduce the above copyright\r
+ notice, this list of conditions and the following disclaimer in the \r
+ documentation and/or other materials provided with the distribution.\r
+ \r
+ * Neither the name of Adobe Systems Incorporated nor the names of its \r
+ contributors may be used to endorse or promote products derived from \r
+ this software without specific prior written permission.\r
+\r
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS\r
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\r
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR \r
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+*/\r
+\r
+package com.adobe.serialization.json \r
+{\r
+\r
+ import flash.utils.describeType;\r
+\r
+ public class JSONEncoder {\r
+ \r
+ /** The string that is going to represent the object we're encoding */\r
+ private var jsonString:String;\r
+ \r
+ /**\r
+ * Creates a new JSONEncoder.\r
+ *\r
+ * @param o The object to encode as a JSON string\r
+ * @langversion ActionScript 3.0\r
+ * @playerversion Flash 9.0\r
+ * @tiptext\r
+ */\r
+ public function JSONEncoder( value:* ) {\r
+ jsonString = convertToString( value );\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Gets the JSON string from the encoder.\r
+ *\r
+ * @return The JSON string representation of the object\r
+ * that was passed to the constructor\r
+ * @langversion ActionScript 3.0\r
+ * @playerversion Flash 9.0\r
+ * @tiptext\r
+ */\r
+ public function getString():String {\r
+ return jsonString;\r
+ }\r
+ \r
+ /**\r
+ * Converts a value to it's JSON string equivalent.\r
+ *\r
+ * @param value The value to convert. Could be any \r
+ * type (object, number, array, etc)\r
+ */\r
+ private function convertToString( value:* ):String {\r
+ \r
+ // determine what value is and convert it based on it's type\r
+ if ( value is String ) {\r
+ \r
+ // escape the string so it's formatted correctly\r
+ return escapeString( value as String );\r
+ \r
+ } else if ( value is Number ) {\r
+ \r
+ // only encode numbers that finate\r
+ return isFinite( value as Number) ? value.toString() : "null";\r
+\r
+ } else if ( value is Boolean ) {\r
+ \r
+ // convert boolean to string easily\r
+ return value ? "true" : "false";\r
+\r
+ } else if ( value is Array ) {\r
+ \r
+ // call the helper method to convert an array\r
+ return arrayToString( value as Array );\r
+ \r
+ } else if ( value is Object && value != null ) {\r
+ \r
+ // call the helper method to convert an object\r
+ return objectToString( value );\r
+ }\r
+ return "null";\r
+ }\r
+ \r
+ /**\r
+ * Escapes a string accoding to the JSON specification.\r
+ *\r
+ * @param str The string to be escaped\r
+ * @return The string with escaped special characters\r
+ * according to the JSON specification\r
+ */\r
+ private function escapeString( str:String ):String {\r
+ // create a string to store the string's jsonstring value\r
+ var s:String = "";\r
+ // current character in the string we're processing\r
+ var ch:String;\r
+ // store the length in a local variable to reduce lookups\r
+ var len:Number = str.length;\r
+ \r
+ // loop over all of the characters in the string\r
+ for ( var i:int = 0; i < len; i++ ) {\r
+ \r
+ // examine the character to determine if we have to escape it\r
+ ch = str.charAt( i );\r
+ switch ( ch ) {\r
+ \r
+ case '"': // quotation mark\r
+ s += "\\\"";\r
+ break;\r
+ \r
+ //case '/': // solidus\r
+ // s += "\\/";\r
+ // break;\r
+ \r
+ case '\\': // reverse solidus\r
+ s += "\\\\";\r
+ break;\r
+ \r
+ case '\b': // bell\r
+ s += "\\b";\r
+ break;\r
+ \r
+ case '\f': // form feed\r
+ s += "\\f";\r
+ break;\r
+ \r
+ case '\n': // newline\r
+ s += "\\n";\r
+ break;\r
+ \r
+ case '\r': // carriage return\r
+ s += "\\r";\r
+ break;\r
+ \r
+ case '\t': // horizontal tab\r
+ s += "\\t";\r
+ break;\r
+ \r
+ default: // everything else\r
+ \r
+ // check for a control character and escape as unicode\r
+ if ( ch < ' ' ) {\r
+ // get the hex digit(s) of the character (either 1 or 2 digits)\r
+ var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );\r
+ \r
+ // ensure that there are 4 digits by adjusting\r
+ // the # of zeros accordingly.\r
+ var zeroPad:String = hexCode.length == 2 ? "00" : "000";\r
+ \r
+ // create the unicode escape sequence with 4 hex digits\r
+ s += "\\u" + zeroPad + hexCode;\r
+ } else {\r
+ \r
+ // no need to do any special encoding, just pass-through\r
+ s += ch;\r
+ \r
+ }\r
+ } // end switch\r
+ \r
+ } // end for loop\r
+ \r
+ return "\"" + s + "\"";\r
+ }\r
+ \r
+ /**\r
+ * Converts an array to it's JSON string equivalent\r
+ *\r
+ * @param a The array to convert\r
+ * @return The JSON string representation of <code>a</code>\r
+ */\r
+ private function arrayToString( a:Array ):String {\r
+ // create a string to store the array's jsonstring value\r
+ var s:String = "";\r
+ \r
+ // loop over the elements in the array and add their converted\r
+ // values to the string\r
+ for ( var i:int = 0; i < a.length; i++ ) {\r
+ // when the length is 0 we're adding the first element so\r
+ // no comma is necessary\r
+ if ( s.length > 0 ) {\r
+ // we've already added an element, so add the comma separator\r
+ s += ","\r
+ }\r
+ \r
+ // convert the value to a string\r
+ s += convertToString( a[i] ); \r
+ }\r
+ \r
+ // KNOWN ISSUE: In ActionScript, Arrays can also be associative\r
+ // objects and you can put anything in them, ie:\r
+ // myArray["foo"] = "bar";\r
+ //\r
+ // These properties aren't picked up in the for loop above because\r
+ // the properties don't correspond to indexes. However, we're\r
+ // sort of out luck because the JSON specification doesn't allow\r
+ // these types of array properties.\r
+ //\r
+ // So, if the array was also used as an associative object, there\r
+ // may be some values in the array that don't get properly encoded.\r
+ //\r
+ // A possible solution is to instead encode the Array as an Object\r
+ // but then it won't get decoded correctly (and won't be an\r
+ // Array instance)\r
+ \r
+ // close the array and return it's string value\r
+ return "[" + s + "]";\r
+ }\r
+ \r
+ /**\r
+ * Converts an object to it's JSON string equivalent\r
+ *\r
+ * @param o The object to convert\r
+ * @return The JSON string representation of <code>o</code>\r
+ */\r
+ private function objectToString( o:Object ):String\r
+ {\r
+ // create a string to store the object's jsonstring value\r
+ var s:String = "";\r
+ \r
+ // determine if o is a class instance or a plain object\r
+ var classInfo:XML = describeType( o );\r
+ if ( classInfo.@name.toString() == "Object" )\r
+ {\r
+ // the value of o[key] in the loop below - store this \r
+ // as a variable so we don't have to keep looking up o[key]\r
+ // when testing for valid values to convert\r
+ var value:Object;\r
+ \r
+ // loop over the keys in the object and add their converted\r
+ // values to the string\r
+ for ( var key:String in o )\r
+ {\r
+ // assign value to a variable for quick lookup\r
+ value = o[key];\r
+ \r
+ // don't add function's to the JSON string\r
+ if ( value is Function )\r
+ {\r
+ // skip this key and try another\r
+ continue;\r
+ }\r
+ \r
+ // when the length is 0 we're adding the first item so\r
+ // no comma is necessary\r
+ if ( s.length > 0 ) {\r
+ // we've already added an item, so add the comma separator\r
+ s += ","\r
+ }\r
+ \r
+ s += escapeString( key ) + ":" + convertToString( value );\r
+ }\r
+ }\r
+ else // o is a class instance\r
+ {\r
+ // Loop over all of the variables and accessors in the class and \r
+ // serialize them along with their values.\r
+ for each ( var v:XML in classInfo..*.( name() == "variable" || name() == "accessor" ) )\r
+ {\r
+ // When the length is 0 we're adding the first item so\r
+ // no comma is necessary\r
+ if ( s.length > 0 ) {\r
+ // We've already added an item, so add the comma separator\r
+ s += ","\r
+ }\r
+ \r
+ s += escapeString( v.@name.toString() ) + ":" \r
+ + convertToString( o[ v.@name ] );\r
+ }\r
+ \r
+ }\r
+ \r
+ return "{" + s + "}";\r
+ }\r
+\r
+ \r
+ }\r
+ \r
+}\r