SequenceParser - JSON decoder jozefchutka forked:0favorite:0lines:520license : MIT License modified : 2011-11-23 17:38:35 Embed Tweet package { import com.adobe.serialization.json.JSON; import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFormat; [SWF(width="465", height="465", frameRate="30", backgroundColor="#ffffff")] public class WonderflApp extends Sprite { private var textField:TextField = new TextField(); public function WonderflApp():void { var source:* = {ab:[1,"b c",{"d\"e":'f\'g', g:-1.1}, true, false]}; var json:String = com.adobe.serialization.json.JSON.encode(source); var result:* = JSON.decode(json); textField.defaultTextFormat = new TextFormat(null, 15); textField.appendText(json); textField.appendText("\n"); textField.appendText("result.ab: " + result.ab.toString()); textField.appendText("\n"); textField.appendText("result.ab[2]['d\"e']: " + result.ab[2]['d\"e'].toString()); textField.appendText("\n"); textField.appendText("result.ab[3]: " + result.ab[3].toString()); textField.appendText("\n"); textField.appendText("result.ab[4]: " + result.ab[4].toString()); textField.width = stage.stageWidth; textField.height = stage.stageHeight; addChild(textField); } } } // ***************************************************** // JSON decoder // ***************************************************** class JSON { private var result:Object; private var pendingDefinition:Definition; public static function decode(source:String):* { var parser:JSON = new JSON; return parser.decode(source); } public function decode(source:String):* { var sequences:Vector.<ISequence> = new Vector.<ISequence>; var sequencesObject:Vector.<ISequence> = new Vector.<ISequence>; var sequencesArray:Vector.<ISequence> = new Vector.<ISequence>; var sequencesDoubleQuota:Vector.<ISequence> = new Vector.<ISequence>; var sequencesEscaped:Vector.<ISequence> = new Vector.<ISequence>; var sequenceAnything:ISequence = new MatchAnythingSequence; var sequenceObject:ISequence = new CustomStartStringEndStringSequence("{", "}", sequencesObject, false, matchObjectStart, matchEnd); var sequenceArray:ISequence = new CustomStartStringEndStringSequence("[", "]", sequencesArray, false, matchArrayStart, matchEnd); var sequenceNumber:ISequence = new MatchRegexpSequence(/^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?/, false, matchNumber); var sequenceDoubleQuota:ISequence = new CustomStartStringEndStringSequence("\"", "\"", sequencesDoubleQuota, false); var sequenceString:ISequence = new MatchAnythingSequence(false, matchChar); var sequenceTrue:ISequence = new MatchStringSequence("true", false, matchTrue); var sequenceFalse:ISequence = new MatchStringSequence("false", false, matchFalse); var sequenceNull:ISequence = new MatchStringSequence("null", false, matchNull); var sequenceObjectColon:ISequence = new MatchStringSequence(":", false, matchObjectColon); var sequenceObjectComma:ISequence = new MatchStringSequence(",", false, matchObjectComma); var sequenceArrayComma:ISequence = new MatchStringSequence(",", false, matchArrayComma); var sequenceEscaped:ISequence = new EscapeSequence("\\", sequencesEscaped); var sequenceEscapedQuota:ISequence = new MatchStringSequence("\"", true, matchEscapedChar); var sequenceEscapedBackslash:ISequence = new MatchStringSequence("\\", true, matchEscapedChar); var sequenceEscapedSlash:ISequence = new MatchStringSequence("/", true, matchEscapedChar); var sequenceEscapedB:ISequence = new MatchStringSequence("b", true, matchEscapedB); var sequenceEscapedF:ISequence = new MatchStringSequence("f", true, matchEscapedF); var sequenceEscapedN:ISequence = new MatchStringSequence("n", true, matchEscapedN); var sequenceEscapedR:ISequence = new MatchStringSequence("r", true, matchEscapedR); var sequenceEscapedT:ISequence = new MatchStringSequence("t", true, matchEscapedT); var sequenceEscapedU:ISequence = new MatchRegexpSequence(/^u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]/, true, matchEscapedU); sequences.push(sequenceObject); sequences.push(sequenceArray); sequences.push(sequenceDoubleQuota); sequences.push(sequenceTrue); sequences.push(sequenceFalse); sequences.push(sequenceNull); sequences.push(sequenceNumber); sequences.push(sequenceAnything); sequencesObject.push(sequenceObjectColon); sequencesObject.push(sequenceObjectComma); append(sequencesObject, sequences); sequencesArray.push(sequenceArrayComma); append(sequencesArray, sequences); sequencesDoubleQuota.push(sequenceEscaped); sequencesDoubleQuota.push(sequenceString); sequencesEscaped.push(sequenceEscapedQuota); sequencesEscaped.push(sequenceEscapedBackslash); sequencesEscaped.push(sequenceEscapedSlash); sequencesEscaped.push(sequenceEscapedB); sequencesEscaped.push(sequenceEscapedF); sequencesEscaped.push(sequenceEscapedN); sequencesEscaped.push(sequenceEscapedR); sequencesEscaped.push(sequenceEscapedT); sequencesEscaped.push(sequenceEscapedU); CustomStartStringEndStringSequence(sequenceObject).cacheSequences(); CustomStartStringEndStringSequence(sequenceArray).cacheSequences(); CustomStartStringEndStringSequence(sequenceDoubleQuota).cacheSequences(); CustomStartStringEndStringSequence(sequenceEscaped).cacheSequences(); SequenceParser.parse(source, sequences); return result; } private static function append(target:Vector.<ISequence>, source:Vector.<ISequence>):void { for each(var item:ISequence in source) target.push(item); } protected function objectProperty(object:Object, property:String):void { } private function matchObjectStart(input:String):void { pendingDefinition = new ObjectDefinition(pendingDefinition); if(result == null) result = pendingDefinition.value; } private function matchArrayStart(input:String):void { pendingDefinition = new ArrayDefinition(pendingDefinition); if(result == null) result = pendingDefinition.value; } private function matchEnd(input:String):void { pendingDefinition.finalize(); if(pendingDefinition.parent) { pendingDefinition.parent.pendingItem.valueObject = pendingDefinition.value; pendingDefinition = pendingDefinition.parent; } } private function matchObjectColon(input:String):void { var objectItem:ObjectItem = ObjectItem(pendingDefinition.pendingItem); objectItem.endName(); objectProperty(pendingDefinition.value, objectItem.name); } private function matchObjectComma(input:String):void { pendingDefinition.finalizePendingItem(); pendingDefinition.pendingItem = new ObjectItem; } private function matchArrayComma(input:String):void { pendingDefinition.finalizePendingItem(); pendingDefinition.pendingItem = new ArrayItem; } private function matchEscapedChar(input:String):void { matchChar(input); } private function matchEscapedB(input:String):void { matchChar("\b"); } private function matchEscapedF(input:String):void { matchChar("\f"); } private function matchEscapedN(input:String):void { matchChar("\n"); } private function matchEscapedR(input:String):void { matchChar("\r"); } private function matchEscapedT(input:String):void { matchChar("\t"); } private function matchEscapedU(input:String):void { var value:String = input.substr(1); matchChar(String.fromCharCode(parseInt(value, 16))); } private function matchTrue(input:String):void { if(pendingDefinition) pendingDefinition.pendingItem.valueBoolean = true; else result = true; } private function matchFalse(input:String):void { if(pendingDefinition) pendingDefinition.pendingItem.valueBoolean = false; else result = false; } private function matchNull(input:String):void { if(pendingDefinition) pendingDefinition.pendingItem.valueObject = null; else result = null; } private function matchChar(input:String):void { if(pendingDefinition) pendingDefinition.pendingItem.valueString = input; else { if(result == null) result = ""; result += input; } } private function matchNumber(input:String):void { var value:Number = parseFloat(input); if(pendingDefinition) pendingDefinition.pendingItem.valueNumber = value; else result = value; } } internal class Definition { public var value:Object; public var pendingItem:Item; private var _parent:Definition; public function Definition(parent:Definition) { _parent = parent; } public function get parent():Definition { return _parent; } public function finalize():void { finalizePendingItem(); } public function finalizePendingItem():void { } } internal class ObjectDefinition extends Definition { public function ObjectDefinition(parent:Definition) { super(parent); value = {}; pendingItem = new ObjectItem; } override public function finalizePendingItem():void { if(pendingItem.isValid) value[(pendingItem as ObjectItem).name] = pendingItem.value; } } internal class ArrayDefinition extends Definition { public function ArrayDefinition(parent:Definition) { super(parent); value = []; pendingItem = new ArrayItem; } override public function finalizePendingItem():void { if(pendingItem.isValid) value.push(pendingItem.value); } } internal class Item { private var _value:Object = ""; public function get value():Object { return _value; } public function get isValid():Boolean { return true; } public function set valueNumber(value:Number):void { _value = value; } public function set valueString(value:String):void { _value += value; } public function set valueObject(value:Object):void { _value = value; } public function set valueBoolean(value:Boolean):void { _value = value; } } internal class ObjectItem extends Item { private var _name:String = ""; private var pendingName:Boolean = true; public function get name():String { return _name; } public function endName():void { if(!pendingName) throw new Error("Unexpected end name."); pendingName = false; } override public function get isValid():Boolean { if(pendingName) throw new Error("Unexpected pending name."); return super.isValid; } override public function set valueNumber(value:Number):void { if(pendingName) throw new Error("Unexpected value."); super.valueNumber = value; } override public function set valueString(value:String):void { if(pendingName) _name += value; else super.valueString = value; } } internal class ArrayItem extends Item { } interface ISequence { function test(input:String):String function get sequences():Vector.<ISequence> function get stopSequence():Boolean } class SequenceParser { public static function parse(input:String, sequences:Vector.<ISequence>):String { if(input == null || !input.length || !sequences || !sequences.length) return input; var source:String = input; var i:uint = 0; while(true) { var sequence:ISequence = sequences[i++]; var match:String = sequence.test(source); if(match != null) { source = source.substr(match.length); source = parse(source, sequence.sequences); if(sequence.stopSequence) break; i = 0; } else if(i == sequences.length) break; } if(source == input) throw new Error("Unmatching sequences"); return source; } } class MatchAnythingSequence implements ISequence { private var _stopSequence:Boolean; private var matchCallback:Function; public function MatchAnythingSequence(stopSequence:Boolean = false, matchCallback:Function = null) { _stopSequence = stopSequence; this.matchCallback = matchCallback; } public function test(input:String):String { if(input == null || input == "") return null; var match:String = input.substr(0, 1); if(matchCallback != null) matchCallback(match); return match; } public function get sequences():Vector.<ISequence> { return null; } public function get stopSequence():Boolean { return _stopSequence; } } class MatchRegexpSequence implements ISequence { private var match:RegExp; private var _stopSequence:Boolean; private var matchCallback:Function; public function MatchRegexpSequence(match:RegExp, stopSequence:Boolean = false, matchCallback:Function = null) { this.match = match; _stopSequence = stopSequence; this.matchCallback = matchCallback; } public function test(input:String):String { var matches:Array = input.match(this.match); if(matches) { var match:String = matches[0]; if(matchCallback != null) matchCallback(match); return match; } return null; } public function get sequences():Vector.<ISequence> { return null; } public function get stopSequence():Boolean { return _stopSequence; } } class MatchStringSequence implements ISequence { private var match:String; private var matchLength:uint; private var _stopSequence:Boolean; private var matchCallback:Function; public function MatchStringSequence(match:String, stopSequence:Boolean = false, matchCallback:Function = null) { this.match = match; matchLength = match.length; _stopSequence = stopSequence; this.matchCallback = matchCallback; } public function test(input:String):String { if(input.substr(0, matchLength) == match) { if(matchCallback != null) matchCallback(match); return match; } return null; } public function get sequences():Vector.<ISequence> { return null; } public function get stopSequence():Boolean { return _stopSequence; } } class StartStringEndStringSequence implements ISequence { private var startSequence:MatchStringSequence; private var endSequence:MatchStringSequence; private var _sequences:Vector.<ISequence>; private var _stopSequence:Boolean; public function StartStringEndStringSequence(start:String, end:String, sequences:Vector.<ISequence> = null, stopSequence:Boolean = false, startCallback:Function = null, endCallback:Function = null) { startSequence = new MatchStringSequence(start, false, startCallback); endSequence = new MatchStringSequence(end, true, endCallback); _sequences = sequences; _stopSequence = stopSequence; } public function get sequences():Vector.<ISequence> { var result:Vector.<ISequence> = _sequences ? _sequences.concat() : new Vector.<ISequence>; result.splice(0, 0, endSequence); return result; } public function test(input:String):String { return startSequence.test(input); } public function get stopSequence():Boolean { return _stopSequence; } } class CustomStartStringEndStringSequence extends StartStringEndStringSequence { protected var cachedSequences:Vector.<ISequence>; public function CustomStartStringEndStringSequence(start:String, end:String, sequences:Vector.<ISequence>=null, stopSequence:Boolean=false, startCallback:Function=null, endCallback:Function=null) { super(start, end, sequences, stopSequence, startCallback, endCallback); } public function cacheSequences():void { cachedSequences = super.sequences; } override public function get sequences():Vector.<ISequence> { return cachedSequences; } } class EscapeSequence extends CustomStartStringEndStringSequence { public function EscapeSequence(start:String, sequences:Vector.<ISequence>=null, topSequence:Boolean=false, startCallback:Function=null, endCallback:Function=null) { super(start, "", sequences, stopSequence, startCallback, endCallback); } override public function cacheSequences():void { super.cacheSequences(); cachedSequences.shift(); } } Code Fullscreen Preview Fullscreen source parent String match Error Boolean Object RegExp Vector Date.parse parseFloat String.fromCharCode parseInt substr concat shift target test toString splice