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