Object Parsing (object from string #2)

Actionscript:
  1. var str:String = '{numA:100, numB:100, stringA:"hi there", object:{red:155, green:155, blue: 255}, array1D:[1, 2, 3, 4, 5, "six"], array2D:[[1,0,0], [0,1,0], [0,0,"z"]], color:0xFF0000}'
  2.  
  3. var obj:Object = toObject(str);//, true); // comment the "true" in for some debug info
  4.  
  5. // acess every value in the object for testing purposes
  6. trace(obj.numA);
  7. trace(obj.numB);
  8. trace(obj.stringA);
  9. trace(obj.object.red, obj.object.green, obj.object.blue);
  10. trace(obj.array1D +" testing access: " + obj.array1D[3]);
  11. trace(obj.array2D + " testing access: " + obj.array2D[1] + "  -  " + obj.array2D[2][2]);
  12. trace(obj.color);
  13.  
  14. /*outputs:
  15. 100
  16. 100
  17. hi there
  18. 155 155 255
  19. 1,2,3,4,5,six testing access: 4
  20. 1,0,0,0,1,0,0,0,z testing access: 0,1,0  -  z
  21. 16711680
  22. */
  23.  
  24. function toObject(str:String, debug:Boolean=false):Object{
  25.     var cmds:Array = new Array();
  26.     var stack:String = "";
  27.     // skip first {
  28.     str = str.replace(/^\s+\{|^\{/, "");
  29.     var obj:Object = new Object();
  30.     for (var i:int = 0; i<str.length; i++){
  31.         var char:String = str.charAt(i);
  32.         if (char == ":"){
  33.             cmds.push(["name:", stack])
  34.             stack = "";
  35.             char = "";
  36.         }else
  37.         if (char == "," || char == "}"){
  38.             if (stack != ""){
  39.               cmds.push(["value:", stack]);
  40.             }
  41.             stack = "";
  42.             char = "";
  43.         }else
  44.         if (char == '"'){
  45.             stack = '"';
  46.             for (j = i + 1; j <str.length; j++){
  47.                 char = str.charAt(j);
  48.                 stack+=char;
  49.                 i++;
  50.                 if (char == '"'){
  51.                      break;
  52.                 }
  53.             }
  54.             cmds.push(["value:", stack]);
  55.             stack = "";
  56.             char = "";
  57.         }else
  58.         if (char == "{"){
  59.             cmds.push(["object:", stack]);
  60.             for (var j:int = i; j <str.length; j++){
  61.                 char = str.charAt(j);
  62.                 stack+=char;
  63.                 i++;
  64.                 if (char == "}"){
  65.                      break;
  66.                 }
  67.             }
  68.             cmds.push(["value:", stack]);
  69.             stack = "";
  70.             char = "";
  71.         }else
  72.         if (char == "["){
  73.             cmds.push(["array:", stack]);
  74.             stack = "";
  75.             char = "";
  76.         }else if (char == "]"){
  77.             if (stack != ""){
  78.                 cmds.push(["value:", stack]);
  79.             }
  80.             stack = "";
  81.             char = "";
  82.             i++;
  83.         }
  84.         stack += char;
  85.     }
  86.     // show some debug info
  87.     if (debug){
  88.         for (i = 0; i<cmds.length; i++) trace(cmds[i]);
  89.     }
  90.     // build the object
  91.     var prop:String;
  92.     var arrayMode:Boolean = false;
  93.     var nest:int = 0;
  94.     for (i = 0; i<cmds.length; i++){
  95.         if (cmds[i][0] == "name:"){
  96.             prop = cmds[i][1].replace(/\s/g, "");
  97.             arrayMode = false;
  98.             nest = 0;
  99.         }else if (cmds[i][0] == "value:"){
  100.             if (arrayMode == false){
  101.               obj[prop] = valueType(cmds[i][1]);
  102.             }else{
  103.                 // populate array
  104.                 if (nest == 1){
  105.                   obj[prop].push(valueType(cmds[i][1]))
  106.                 }else{
  107.                   obj[prop][nest - 2].push(valueType(cmds[i][1]))
  108.                 }
  109.             }
  110.         }else if (cmds[i][0] == "object:"){
  111.             i++;
  112.             obj[prop] = toObject(cmds[i][1], debug);
  113.         }else if (cmds[i][0] == "array:"){
  114.             arrayMode = true;
  115.             nest++;
  116.             if (nest == 1){
  117.                 // create array
  118.                 obj[prop] = new Array()
  119.             }else{
  120.                 // nested array
  121.                 obj[prop].push(new Array());
  122.             }
  123.         }
  124.     }
  125.     return obj;
  126. }
  127.  
  128. function valueType(val:*):* {
  129.     if (isNaN(Number(val))) {
  130.            // remove leading and trailing white
  131.            // remove "" around strings
  132.            val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
  133.            //  see if it's a boolean
  134.            if (val == "true"){
  135.                val = true;
  136.            }else if (val == "false"){
  137.                val = false
  138.            }
  139.     } else {
  140.         val = Number(val);
  141.     }
  142.     return val;
  143. }

This rather large code snippet parses ActionScript style Object syntax. While this is by no means a complete parser - it was easy to write and saved me a great deal of time on a project recently....

I specifically needed support for 1D and 2D arrays as Object properties. If that weren't the case I would have used something simple like yesterdays method. The array support won't work with anything higher than a 2D array (for now... may fix that soon). For fun I also added nested Object support - this doesn't have any dimensional limitation so you can nest as many objects as you want. For instance.. this works just fine:

Actionscript:
  1. var gameUser:Object = toObject('{name:"Mr.S", alive:true, stats:{lives:100, points:1200, data:[100,12,40,12,90], plant:{branch:{leaves:["red, "green", "blue"]}}}');
  2.  
  3. trace(gameUser.stats.plant.branch.leaves[0]);
  4. trace(gameUser.stats.lives);
  5. // boolean support
  6. trace("not true is false: ", !gameUser.alive);
  7. /*outputs:
  8. red
  9. 100
  10. not true is false:  false
  11. */

You'll also notice the added boolean support in the above example. Which was suggested by katopz in the comments of yesterdays post.

Definitely need to add a word of warning here... I wouldn't be surprised if there were a bug or two lurking in this code. The Objects I've been parsing with it are all quite similar... so use at your own risk.

UPDATEs - Known Issues:
1) fixed a bug where if a String property contains a ,{}[] or : character the parser failed.

This entry was posted in dynamic, string manipulation, strings and tagged , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

4 Comments

  1. Posted April 25, 2009 at 6:31 am | Permalink

    Ha ha … great. I’ve tested with very long strings returned from php script… Working great. Excelent work.

  2. Posted April 25, 2009 at 7:01 am | Permalink

    cool…. glad to here it’s working nicely with longer strings :)

  3. Posted November 22, 2010 at 12:53 am | Permalink

    The particular syntax of that is called JSON: JavaScript Object Notation, and what you’ve created is a rudimentary JSON parser, with a looser idea of the syntax (object properties should be strings in JSON).

    It’s a great format, similar to XML, only more dense, but just as widely (if not more so) supported. However, if you do use this for something, I’d suggest you go with something a little more advanced: [shamelessplug] https://github.com/skyboy/AS3-Utilities/blob/master/skyboy/text/JSON.as [/shamelessplug]

    Interesting approach though.

  4. Posted November 22, 2010 at 3:54 pm | Permalink

    thanks for the feedback skyboy. I’ll be sure to checkout your json parser.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*