annotate bindings/as3/ext/com/adobe/serialization/json/JSONEncoder.as @ 770:c54bc2ffbf92 tip

update tags
author convert-repo
date Fri, 16 Dec 2011 11:34:01 +0000
parents 3a0b9700b3d2
children
rev   line source
mas01mj@732 1 /*
mas01mj@732 2 Copyright (c) 2008, Adobe Systems Incorporated
mas01mj@732 3 All rights reserved.
mas01mj@732 4
mas01mj@732 5 Redistribution and use in source and binary forms, with or without
mas01mj@732 6 modification, are permitted provided that the following conditions are
mas01mj@732 7 met:
mas01mj@732 8
mas01mj@732 9 * Redistributions of source code must retain the above copyright notice,
mas01mj@732 10 this list of conditions and the following disclaimer.
mas01mj@732 11
mas01mj@732 12 * Redistributions in binary form must reproduce the above copyright
mas01mj@732 13 notice, this list of conditions and the following disclaimer in the
mas01mj@732 14 documentation and/or other materials provided with the distribution.
mas01mj@732 15
mas01mj@732 16 * Neither the name of Adobe Systems Incorporated nor the names of its
mas01mj@732 17 contributors may be used to endorse or promote products derived from
mas01mj@732 18 this software without specific prior written permission.
mas01mj@732 19
mas01mj@732 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
mas01mj@732 21 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
mas01mj@732 22 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
mas01mj@732 23 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
mas01mj@732 24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
mas01mj@732 25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
mas01mj@732 26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
mas01mj@732 27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
mas01mj@732 28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
mas01mj@732 29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
mas01mj@732 30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mas01mj@732 31 */
mas01mj@732 32
mas01mj@732 33 package com.adobe.serialization.json
mas01mj@732 34 {
mas01mj@732 35
mas01mj@732 36 import flash.utils.describeType;
mas01mj@732 37
mas01mj@732 38 public class JSONEncoder {
mas01mj@732 39
mas01mj@732 40 /** The string that is going to represent the object we're encoding */
mas01mj@732 41 private var jsonString:String;
mas01mj@732 42
mas01mj@732 43 /**
mas01mj@732 44 * Creates a new JSONEncoder.
mas01mj@732 45 *
mas01mj@732 46 * @param o The object to encode as a JSON string
mas01mj@732 47 * @langversion ActionScript 3.0
mas01mj@732 48 * @playerversion Flash 9.0
mas01mj@732 49 * @tiptext
mas01mj@732 50 */
mas01mj@732 51 public function JSONEncoder( value:* ) {
mas01mj@732 52 jsonString = convertToString( value );
mas01mj@732 53
mas01mj@732 54 }
mas01mj@732 55
mas01mj@732 56 /**
mas01mj@732 57 * Gets the JSON string from the encoder.
mas01mj@732 58 *
mas01mj@732 59 * @return The JSON string representation of the object
mas01mj@732 60 * that was passed to the constructor
mas01mj@732 61 * @langversion ActionScript 3.0
mas01mj@732 62 * @playerversion Flash 9.0
mas01mj@732 63 * @tiptext
mas01mj@732 64 */
mas01mj@732 65 public function getString():String {
mas01mj@732 66 return jsonString;
mas01mj@732 67 }
mas01mj@732 68
mas01mj@732 69 /**
mas01mj@732 70 * Converts a value to it's JSON string equivalent.
mas01mj@732 71 *
mas01mj@732 72 * @param value The value to convert. Could be any
mas01mj@732 73 * type (object, number, array, etc)
mas01mj@732 74 */
mas01mj@732 75 private function convertToString( value:* ):String {
mas01mj@732 76
mas01mj@732 77 // determine what value is and convert it based on it's type
mas01mj@732 78 if ( value is String ) {
mas01mj@732 79
mas01mj@732 80 // escape the string so it's formatted correctly
mas01mj@732 81 return escapeString( value as String );
mas01mj@732 82
mas01mj@732 83 } else if ( value is Number ) {
mas01mj@732 84
mas01mj@732 85 // only encode numbers that finate
mas01mj@732 86 return isFinite( value as Number) ? value.toString() : "null";
mas01mj@732 87
mas01mj@732 88 } else if ( value is Boolean ) {
mas01mj@732 89
mas01mj@732 90 // convert boolean to string easily
mas01mj@732 91 return value ? "true" : "false";
mas01mj@732 92
mas01mj@732 93 } else if ( value is Array ) {
mas01mj@732 94
mas01mj@732 95 // call the helper method to convert an array
mas01mj@732 96 return arrayToString( value as Array );
mas01mj@732 97
mas01mj@732 98 } else if ( value is Object && value != null ) {
mas01mj@732 99
mas01mj@732 100 // call the helper method to convert an object
mas01mj@732 101 return objectToString( value );
mas01mj@732 102 }
mas01mj@732 103 return "null";
mas01mj@732 104 }
mas01mj@732 105
mas01mj@732 106 /**
mas01mj@732 107 * Escapes a string accoding to the JSON specification.
mas01mj@732 108 *
mas01mj@732 109 * @param str The string to be escaped
mas01mj@732 110 * @return The string with escaped special characters
mas01mj@732 111 * according to the JSON specification
mas01mj@732 112 */
mas01mj@732 113 private function escapeString( str:String ):String {
mas01mj@732 114 // create a string to store the string's jsonstring value
mas01mj@732 115 var s:String = "";
mas01mj@732 116 // current character in the string we're processing
mas01mj@732 117 var ch:String;
mas01mj@732 118 // store the length in a local variable to reduce lookups
mas01mj@732 119 var len:Number = str.length;
mas01mj@732 120
mas01mj@732 121 // loop over all of the characters in the string
mas01mj@732 122 for ( var i:int = 0; i < len; i++ ) {
mas01mj@732 123
mas01mj@732 124 // examine the character to determine if we have to escape it
mas01mj@732 125 ch = str.charAt( i );
mas01mj@732 126 switch ( ch ) {
mas01mj@732 127
mas01mj@732 128 case '"': // quotation mark
mas01mj@732 129 s += "\\\"";
mas01mj@732 130 break;
mas01mj@732 131
mas01mj@732 132 //case '/': // solidus
mas01mj@732 133 // s += "\\/";
mas01mj@732 134 // break;
mas01mj@732 135
mas01mj@732 136 case '\\': // reverse solidus
mas01mj@732 137 s += "\\\\";
mas01mj@732 138 break;
mas01mj@732 139
mas01mj@732 140 case '\b': // bell
mas01mj@732 141 s += "\\b";
mas01mj@732 142 break;
mas01mj@732 143
mas01mj@732 144 case '\f': // form feed
mas01mj@732 145 s += "\\f";
mas01mj@732 146 break;
mas01mj@732 147
mas01mj@732 148 case '\n': // newline
mas01mj@732 149 s += "\\n";
mas01mj@732 150 break;
mas01mj@732 151
mas01mj@732 152 case '\r': // carriage return
mas01mj@732 153 s += "\\r";
mas01mj@732 154 break;
mas01mj@732 155
mas01mj@732 156 case '\t': // horizontal tab
mas01mj@732 157 s += "\\t";
mas01mj@732 158 break;
mas01mj@732 159
mas01mj@732 160 default: // everything else
mas01mj@732 161
mas01mj@732 162 // check for a control character and escape as unicode
mas01mj@732 163 if ( ch < ' ' ) {
mas01mj@732 164 // get the hex digit(s) of the character (either 1 or 2 digits)
mas01mj@732 165 var hexCode:String = ch.charCodeAt( 0 ).toString( 16 );
mas01mj@732 166
mas01mj@732 167 // ensure that there are 4 digits by adjusting
mas01mj@732 168 // the # of zeros accordingly.
mas01mj@732 169 var zeroPad:String = hexCode.length == 2 ? "00" : "000";
mas01mj@732 170
mas01mj@732 171 // create the unicode escape sequence with 4 hex digits
mas01mj@732 172 s += "\\u" + zeroPad + hexCode;
mas01mj@732 173 } else {
mas01mj@732 174
mas01mj@732 175 // no need to do any special encoding, just pass-through
mas01mj@732 176 s += ch;
mas01mj@732 177
mas01mj@732 178 }
mas01mj@732 179 } // end switch
mas01mj@732 180
mas01mj@732 181 } // end for loop
mas01mj@732 182
mas01mj@732 183 return "\"" + s + "\"";
mas01mj@732 184 }
mas01mj@732 185
mas01mj@732 186 /**
mas01mj@732 187 * Converts an array to it's JSON string equivalent
mas01mj@732 188 *
mas01mj@732 189 * @param a The array to convert
mas01mj@732 190 * @return The JSON string representation of <code>a</code>
mas01mj@732 191 */
mas01mj@732 192 private function arrayToString( a:Array ):String {
mas01mj@732 193 // create a string to store the array's jsonstring value
mas01mj@732 194 var s:String = "";
mas01mj@732 195
mas01mj@732 196 // loop over the elements in the array and add their converted
mas01mj@732 197 // values to the string
mas01mj@732 198 for ( var i:int = 0; i < a.length; i++ ) {
mas01mj@732 199 // when the length is 0 we're adding the first element so
mas01mj@732 200 // no comma is necessary
mas01mj@732 201 if ( s.length > 0 ) {
mas01mj@732 202 // we've already added an element, so add the comma separator
mas01mj@732 203 s += ","
mas01mj@732 204 }
mas01mj@732 205
mas01mj@732 206 // convert the value to a string
mas01mj@732 207 s += convertToString( a[i] );
mas01mj@732 208 }
mas01mj@732 209
mas01mj@732 210 // KNOWN ISSUE: In ActionScript, Arrays can also be associative
mas01mj@732 211 // objects and you can put anything in them, ie:
mas01mj@732 212 // myArray["foo"] = "bar";
mas01mj@732 213 //
mas01mj@732 214 // These properties aren't picked up in the for loop above because
mas01mj@732 215 // the properties don't correspond to indexes. However, we're
mas01mj@732 216 // sort of out luck because the JSON specification doesn't allow
mas01mj@732 217 // these types of array properties.
mas01mj@732 218 //
mas01mj@732 219 // So, if the array was also used as an associative object, there
mas01mj@732 220 // may be some values in the array that don't get properly encoded.
mas01mj@732 221 //
mas01mj@732 222 // A possible solution is to instead encode the Array as an Object
mas01mj@732 223 // but then it won't get decoded correctly (and won't be an
mas01mj@732 224 // Array instance)
mas01mj@732 225
mas01mj@732 226 // close the array and return it's string value
mas01mj@732 227 return "[" + s + "]";
mas01mj@732 228 }
mas01mj@732 229
mas01mj@732 230 /**
mas01mj@732 231 * Converts an object to it's JSON string equivalent
mas01mj@732 232 *
mas01mj@732 233 * @param o The object to convert
mas01mj@732 234 * @return The JSON string representation of <code>o</code>
mas01mj@732 235 */
mas01mj@732 236 private function objectToString( o:Object ):String
mas01mj@732 237 {
mas01mj@732 238 // create a string to store the object's jsonstring value
mas01mj@732 239 var s:String = "";
mas01mj@732 240
mas01mj@732 241 // determine if o is a class instance or a plain object
mas01mj@732 242 var classInfo:XML = describeType( o );
mas01mj@732 243 if ( classInfo.@name.toString() == "Object" )
mas01mj@732 244 {
mas01mj@732 245 // the value of o[key] in the loop below - store this
mas01mj@732 246 // as a variable so we don't have to keep looking up o[key]
mas01mj@732 247 // when testing for valid values to convert
mas01mj@732 248 var value:Object;
mas01mj@732 249
mas01mj@732 250 // loop over the keys in the object and add their converted
mas01mj@732 251 // values to the string
mas01mj@732 252 for ( var key:String in o )
mas01mj@732 253 {
mas01mj@732 254 // assign value to a variable for quick lookup
mas01mj@732 255 value = o[key];
mas01mj@732 256
mas01mj@732 257 // don't add function's to the JSON string
mas01mj@732 258 if ( value is Function )
mas01mj@732 259 {
mas01mj@732 260 // skip this key and try another
mas01mj@732 261 continue;
mas01mj@732 262 }
mas01mj@732 263
mas01mj@732 264 // when the length is 0 we're adding the first item so
mas01mj@732 265 // no comma is necessary
mas01mj@732 266 if ( s.length > 0 ) {
mas01mj@732 267 // we've already added an item, so add the comma separator
mas01mj@732 268 s += ","
mas01mj@732 269 }
mas01mj@732 270
mas01mj@732 271 s += escapeString( key ) + ":" + convertToString( value );
mas01mj@732 272 }
mas01mj@732 273 }
mas01mj@732 274 else // o is a class instance
mas01mj@732 275 {
mas01mj@732 276 // Loop over all of the variables and accessors in the class and
mas01mj@732 277 // serialize them along with their values.
mas01mj@732 278 for each ( var v:XML in classInfo..*.(
mas01mj@732 279 name() == "variable"
mas01mj@732 280 ||
mas01mj@732 281 (
mas01mj@732 282 name() == "accessor"
mas01mj@732 283 // Issue #116 - Make sure accessors are readable
mas01mj@732 284 && (""+attribute( "access" )).charAt( 0 ) == "r" )
mas01mj@732 285 ) )
mas01mj@732 286 {
mas01mj@732 287 // Issue #110 - If [Transient] metadata exists, then we should skip
mas01mj@732 288 if ( v.metadata && v.metadata.( @name == "Transient" ).length() > 0 )
mas01mj@732 289 {
mas01mj@732 290 continue;
mas01mj@732 291 }
mas01mj@732 292
mas01mj@732 293 // When the length is 0 we're adding the first item so
mas01mj@732 294 // no comma is necessary
mas01mj@732 295 if ( s.length > 0 ) {
mas01mj@732 296 // We've already added an item, so add the comma separator
mas01mj@732 297 s += ","
mas01mj@732 298 }
mas01mj@732 299
mas01mj@732 300 s += escapeString( v.@name.toString() ) + ":"
mas01mj@732 301 + convertToString( o[ v.@name ] );
mas01mj@732 302 }
mas01mj@732 303
mas01mj@732 304 }
mas01mj@732 305
mas01mj@732 306 return "{" + s + "}";
mas01mj@732 307 }
mas01mj@732 308
mas01mj@732 309
mas01mj@732 310 }
mas01mj@732 311
mas01mj@732 312 }