转自:http://blog.sina.com.cn/s/blog_7cf671ed0100qdc8.html
Source Code:
JSON.as
package framework.json {
import flash.utils.describeType;
public final class JSON {
public static function encode(object:*):String {
return new JSONEncoder(object).encode();
}
public static function decode(jsonString:String):* {
return new JSONDecoder(jsonString).decode();
}
internal static const TYPE_ARRAY:String = "Array";
internal static const TYPE_BOOLEAN:String = "Boolean";
internal static const TYPE_DATE:String = "Date";
internal static const TYPE_NUMBER:String = "Number";
internal static const TYPE_INT:String = "int";
internal static const TYPE_UINT:String = "uint";
internal static const TYPE_STRING:String = "String";
internal static const VALUE_NULL:String = "null";
internal static const VALUE_TRUE:String = "true";
internal static const VALUE_FALSE:String = "false";
internal static const SYMBOL_LEFT_BRACE:String = '{';
internal static const SYMBOL_RIGHT_BRACE:String = '}';
internal static const SYMBOL_LEFT_BRACKET:String = '[';
internal static const SYMBOL_RIGHT_BRACKET:String = ']';
internal static const SYMBOL_COLON:String = ':';
internal static const SYMBOL_COMMA:String = ',';
internal static const SYMBOL_QUOTE:String = '"';
internal static const SYMBOL_T:String = 't';
internal static const SYMBOL_F:String = 'f';
internal static const SYMBOL_CLASS:String = 'class';
internal static const WRITE_ONLY_FLAG:String = "writeonly";
internal static function getProperties(o:Object):XMLList {
var typeDescription:XML = describeType(o);
return typeDescription..accessor.(@access != WRITE_ONLY_FLAG) + typeDescription..variable;
}
public function JSON(privateJSON:PrivateJSON) {}
}
}
class PrivateJSON {}
JSONEncoder.as
package framework.json {
import flash.utils.getQualifiedClassName;
internal final class JSONEncoder {
private var _obj:*;
public function JSONEncoder(object:*) {
_obj = object;
}
public function encode():String {
var r:String;
if (_obj is String)
r = encodeString(_obj as String);
else if (_obj is Number)
r = encodeNumber(_obj as Number);
else if (_obj is Date)
r = encodeDate(_obj as Date);
else if (_obj is Boolean)
r = encodeBoolean(_obj as Boolean);
else if (_obj is Array)
r = encodeArray(_obj as Array);
else if (_obj is Object)
r = encodeObject(_obj);
else
r = JSON.VALUE_NULL;
return r;
}
private function encodeString(s:String):String {
return JSON.SYMBOL_QUOTE + escape(s) + JSON.SYMBOL_QUOTE;
}
private function encodeNumber(n:Number):String {
return isFinite(n) ? n.toString() : JSON.VALUE_NULL;
}
private function encodeBoolean(b:Boolean):String {
return b ? JSON.VALUE_TRUE : JSON.VALUE_FALSE;
}
private function encodeDate(d:Date):String {
return d.getTime().toString();
}
private function encodeArray(a:Array):String {
var r:String = JSON.SYMBOL_LEFT_BRACKET;
if (a.length > 0) {
for each (var v:* in a) {
if (v != null) {
if (v is String)
r += encodeString(v as String);
else if (v is Number)
r += encodeNumber(v as Number);
else if (v is Date)
r += encodeDate(v as Date);
else if (v is Boolean)
r += encodeBoolean(v as Boolean);
else if (v is Array)
r += encodeArray(v as Array);
else
r += encodeObject(v);
r += JSON.SYMBOL_COMMA;
}
}
r = r.substr(0, r.length - 1);
}
return r + JSON.SYMBOL_RIGHT_BRACKET;
}
private function encodeObject(o:Object):String {
var r:String = JSON.SYMBOL_LEFT_BRACE + JSON.SYMBOL_QUOTE + JSON.SYMBOL_CLASS + JSON.SYMBOL_QUOTE + JSON.SYMBOL_COLON + JSON.SYMBOL_QUOTE + escape(getQualifiedClassName(o).replace("::", ".")) + JSON.SYMBOL_QUOTE + JSON.SYMBOL_COMMA;
var properties:XMLList = JSON.getProperties(o);
var propertyName:String;
var propertyType:String;
var propertyValue:*;
for each (var property:XML in properties) {
propertyName = property.@name;
propertyType = property.@type;
propertyValue = o[propertyName];
r += JSON.SYMBOL_QUOTE;
r += propertyName;
r += JSON.SYMBOL_QUOTE;
r += JSON.SYMBOL_COLON;
if (propertyValue == null)
r += JSON.VALUE_NULL;
else {
if (propertyType == JSON.TYPE_STRING)
r += encodeString(propertyValue as String);
else if ((propertyType == JSON.TYPE_NUMBER) || (propertyType == JSON.TYPE_INT) || (propertyType == JSON.TYPE_UINT))
r += encodeNumber(propertyValue as Number);
else if (propertyType == JSON.TYPE_DATE)
r += encodeDate(propertyValue as Date);
else if (propertyType == JSON.TYPE_BOOLEAN)
r += encodeBoolean(propertyValue as Boolean);
else if (propertyType == JSON.TYPE_ARRAY)
r += encodeArray(propertyValue as Array);
else
r += encodeObject(propertyValue);
}
r += JSON.SYMBOL_COMMA;
}
return r.substr(0, r.length - 1) + JSON.SYMBOL_RIGHT_BRACE;
}
}
}
JSONDecoder.as
package framework.json {
import flash.utils.getDefinitionByName;
internal final class JSONDecoder {
private var _jsonString:String;
private var _jsonLength:int;
private var _position:int;
private var _currentChar:String;
public function JSONDecoder(jsonString:String) {
_jsonString = jsonString;
_jsonLength = jsonString.length;
_position = 0;
nextChar();
skipWhiteSpace();
}
public function decode():* {
var s:String;
var r:Object;
switch(_currentChar) {
case JSON.SYMBOL_LEFT_BRACE:
r = parseObject();
break;
case JSON.SYMBOL_LEFT_BRACKET:
r = parseArray();
break;
case JSON.SYMBOL_T:
case JSON.SYMBOL_F:
r = parseBoolean();
break;
case JSON.SYMBOL_QUOTE:
r = parseString();
break;
default:
r = parseNumber();
}
if (r != null)
r = convert(r);
return r;
}
private function parseObject():Object {
var r:Object = new Object();
var n:String;
var i:int;
nextChar();
while (!(_currentChar == JSON.SYMBOL_RIGHT_BRACE || _position >= _jsonLength)) {
n = "";
while (_currentChar != JSON.SYMBOL_COLON) {
if (_currentChar == JSON.SYMBOL_RIGHT_BRACE || _position >= _jsonLength) {
nextChar();
return r;
}
n += _currentChar;
nextChar();
}
if ((i = n.lastIndexOf(JSON.SYMBOL_COMMA)) != -1)
n = n.substr(i + 1);
n = fixPropertyName(n);
nextChar();
skipWhiteSpace();
switch (_currentChar) {
case JSON.SYMBOL_QUOTE:
r[n] = parseString();
break;
case JSON.SYMBOL_T:
case JSON.SYMBOL_F:
r[n] = parseBoolean();
break;
case JSON.SYMBOL_LEFT_BRACKET:
r[n] = parseArray();
break;
case JSON.SYMBOL_LEFT_BRACE:
r[n] = parseObject();
break;
case JSON.SYMBOL_COMMA:
r[n] = null;
break;
default:
r[n] = parseNumber();
}
}
nextChar();
return r;
}
private function parseArray():Array {
var r:Array = new Array();
nextChar();
while (!(_currentChar == JSON.SYMBOL_RIGHT_BRACKET || _position >= _jsonLength)) {
switch (_currentChar) {
case JSON.SYMBOL_QUOTE:
r.push(parseString());
break;
case JSON.SYMBOL_T:
case JSON.SYMBOL_F:
r.push(parseBoolean());
break;
case JSON.SYMBOL_LEFT_BRACKET:
r.push(parseArray());
break;
case JSON.SYMBOL_LEFT_BRACE:
r.push(parseObject());
break;
default:
r.push(parseNumber());
}
while (_currentChar != JSON.SYMBOL_COMMA) {
if (_currentChar == JSON.SYMBOL_RIGHT_BRACKET || _position >= _jsonLength) {
nextChar();
return r;
}
nextChar();
}
nextChar();
skipWhiteSpace();
}
nextChar();
return r;
}
private function parseNumber():Object {
var r:Object;
if (isDigit(_currentChar) || _currentChar == '-')
r = readNumber();
return r;
}
private function parseBoolean():Object {
var r:Object;
var s:String = _currentChar + nextChar() + nextChar() + nextChar();
if (s == JSON.VALUE_TRUE)
r = Boolean(true);
else {
s += nextChar();
if (s == JSON.VALUE_FALSE)
r = Boolean(false);
}
return r;
}
private function parseString():Object {
var r:Object;
var idx:int = _jsonString.indexOf(JSON.SYMBOL_QUOTE, _position);
if (idx == -1)
_position = _jsonLength;
else {
r = (_jsonString.substring(_position, idx));
_position = idx + 1;
}
nextChar();
return r;
}
private function nextChar():String {
return _currentChar = _jsonString.charAt(_position++);
}
private function skipWhiteSpace():void {
while ((_currentChar == ' ') || (_currentChar == '\t') || (_currentChar == '\n') || (_currentChar == '\r')) {
nextChar();
if (_position >= _jsonLength)
break;
}
}
private function readNumber():Object {
var s:String = "";
if (_currentChar == '-') {
s += _currentChar;
nextChar();
}
if (!isDigit(_currentChar)) return null;
if (_currentChar == '0') {
s += _currentChar;
nextChar();
if (isDigit(_currentChar)) return null;
} else {
while (isDigit(_currentChar)) {
s += _currentChar;
nextChar();
}
}
if (_currentChar == '.') {
s += _currentChar;
nextChar();
if (!isDigit(_currentChar)) return null;
while (isDigit(_currentChar)) {
s += _currentChar;
nextChar();
}
}
if (_currentChar == 'e' || _currentChar == 'E') {
s += _currentChar;
nextChar();
if (_currentChar == '+' || _currentChar == '-') {
s += _currentChar;
nextChar();
if (!isDigit(_currentChar)) return null;
while (isDigit(_currentChar)) {
s += _currentChar;
nextChar();
}
}
}
return Number(s);
}
private static function isDigit(ch:String):Boolean {
return ( ch >= '0' && ch <= '9' );
}
private static function fixPropertyName(name:String):String {
var r:String = "";
var s:String;
var loops:int = name.length;
for (var i:int = 0; i < loops; i++) {
s = name.charAt(i);
if (!((s == ' ') || (s == '\t') || (s == '\n') || (s == '\r') || (s == '"') || (s == '\'')))
r += s;
}
return r;
}
private static function convertArray(a:Array):Array {
var r:Array = new Array();
for each (var e:* in a) {
if ((e is String) || (e is Number) || (e is Date) || (e is Boolean))
r.push(e);
else if (e is Array)
r.push(convertArray(e));
else if (e is Object)
r.push(convertObject(e));
}
return r;
}
private static function convertObject(obj:Object):* {
var r:Object;
var clsName:String = obj[JSON.SYMBOL_CLASS];
if (clsName != null) {
try {
var clazz:Class = getDefinitionByName((clsName)) as Class;
var o:Object = new clazz();
var properties:XMLList = JSON.getProperties(o);
var propertyName:String;
var propertyType:String;
var propertyValue:*;
var dateProperty:Date;
for each (var property:XML in properties) {
propertyName = property.@name;
propertyType = property.@type;
propertyValue = obj[propertyName];
if ((propertyType == JSON.TYPE_STRING) ||
(propertyType == JSON.TYPE_NUMBER) ||
(propertyType == JSON.TYPE_INT) ||
(propertyType == JSON.TYPE_UINT) ||
(propertyType == JSON.TYPE_BOOLEAN)) {
o[propertyName] = propertyValue;
} else if (propertyType == JSON.TYPE_DATE) {
dateProperty = new Date();
dateProperty.setTime(propertyValue);
o[propertyName] = dateProperty;
} else if (propertyType == JSON.TYPE_ARRAY) {
if (propertyValue != null)
o[propertyName] = convertArray(propertyValue);
} else {
if (propertyValue != null)
o[propertyName] = convertObject(propertyValue);
}
}
r = o;
} catch (e:Error) {
trace(e);
}
} else
r = obj;
return r;
}
private static function convert(obj:Object):* {
var r:Object;
if ((obj is String) || (obj is Number) || (obj is Date) || (obj is Boolean))
r = obj;
else if (obj is Array)
r = convertArray(obj as Array);
else if (obj is Object)
r = convertObject(obj);
else
r = obj;
return r;
}
}
}