Check out "Do you speak JavaScript?" - my latest video course on advanced JavaScript.
Language APIs, Popular Concepts, Design Patterns, Advanced Techniques In the Browser

AS3: converting XML to JSON object

I'm absolutely sure that you are using external data in your flash/flex applications. It is a good practice to transfer information in XML format. Most of the projects that I'm working on also use XML and in most of them I have a class that converts the data to JSON object. The problem with this workflow is that the parser's logic is always different because the XML is different. These days I wrote a class that solved this problem and directly converted every given XML file to a JSON object.

The XML file that I tested with:

<xml>
  <settings>
    <color>#990022</color>
    <size>12</size>
    <effects>
      <blur>yes</blur>
      <glow>no</glow>
      <drop-shadow>no</drop-shadow>
    </effects>
  </settings>	
  <images>
    <base-url>http://site.com/pics/upload</base-url>
    <img id="image_01">
      <label><![CDATA[label of image 01]]></label>
      <url><![CDATA[url of image 01]]></url>
    </img>
    <img id="image_02">
      <label><![CDATA[label of image 02]]></label>
      <url><![CDATA[url of image 02]]></url>
    </img>
    <img id="image_03">
      <label>
        <en><![CDATA[English version]]></en>
        <bg><![CDATA[Bulgarian version]]></bg>
        <it><![CDATA[Italian version]]></it>
      </label>
      <url><![CDATA[url of image 03]]></url>
    </img>
  </images>
  <siteURL>http://site.com</siteURL>
  <user>
    <id session="3ddfa331fd1393029">952</id>
  </user>
</xml>

The result:

{
  "settings": {
    "color": "#990022",
    "effects": {
      "glow": "no",
      "blur": "yes",
      "drop-shadow": "no"
    },
    "size": "12"
  },
  "user": {
    "id": {
      "session": "3ddfa331fd1393029",
      "_content": "952"
    }
  },
  "images": {
    "img": [
      { "url": "url of image 01", "id": "image_01", "label": "label of image 01"},
      { "url": "url of image 02", "id": "image_02", "label": "label of image 02"},
      { 
        "url": "url of image 03",
        "id": "image_03",
        "label": {
          "bg": "Bulgarian version", "en": "English version", "it": "Italian version"
        }
      }
    ],
    "base-url": "http://site.com/pics/upload"
  },
  "siteURL": "http://site.com"
}

The usage:

var data:Object = XML2JSON.parse(new XML(...xml string here...));

There are two things that you have to pay attention to:

  • 1. When you have a node which doesn't have children (i.e. a text node), but it has attributes. For example:

    <id session="3ddfa331fd1393029">952</id>
    

    is converted to:

    {
      "session": "3ddfa331fd1393029"
      "_content": "952"
    }
    

    I.e. the text in the node is attached to _content property.

  • 2. When you have a node which should be an array of objects, but currently it has only one child. In this case the script will not recognize your child as an element of an array, but will add it as a property. That's why you should describe those nodes in arrays property of XML2JSON class. For example:

    <images>
      <img>
        <label>label of image</label>
      </img>
    </image>
    

    will be converted to:

    {
      "images": {
        "img": {
          "label": "label of image"
        }
      }
    }
    

Mark img node as an array:

XML2JSON.arrays = ["img"];
XML2JSON.parse(new XML(...xml string here...));

And the result will be:

{
  "images": {
    "img": [
      {
        "label": "label of image"
      }
    ]
  }
}

And here is the code of the class:

package {
  public class XML2JSON {
   private static
   var _arrays: Array;
   public static
   function parse(node: * ): Object {
    var obj: Object = {};
    var numOfChilds: int = node.children().length();
    for (var i: int = 0; i < numOfChilds; i++) {
     var childNode: * = node.children()[i];
     var childNodeName: String = childNode.name();
     var value: * ;
     if (childNode.children().length() == 1 && childNode.children()[0].name() == null) {
      if (childNode.attributes().length() > 0) {
       value = {
        _content: childNode.children()[0].toString()
       };
       var numOfAttributes: int = childNode.attributes().length();
       for (var j: int = 0; j < numOfAttributes; j++) {
        value[childNode.attributes()[j].name().toString()] = childNode.attributes()[j];
       }
      } else {
       value = childNode.children()[0].toString();
      }
     } else {
      value = parse(childNode);
     }
     if (obj[childNodeName]) {
      if (getTypeof(obj[childNodeName]) == "array") {
       obj[childNodeName].push(value);
      } else {
       obj[childNodeName] = [obj[childNodeName], value];
      }
     } else if (isArray(childNodeName)) {
      obj[childNodeName] = [value];
     } else {
      obj[childNodeName] = value;
     }
    }
    numOfAttributes = node.attributes().length();
    for (i = 0; i < numOfAttributes; i++) {
     obj[node.attributes()[i].name().toString()] = node.attributes()[i];
    }
    if (numOfChilds == 0) {
     if (numOfAttributes == 0) {
      obj = "";
     } else {
      obj._content = "";
     }
    }
    return obj;
   }
   public static
   function get arrays(): Array {
    if (!_arrays) {
     _arrays = [];
    }
    return _arrays;
   }
   public static
   function set arrays(a: Array): void {
    _arrays = a;
   }
   private static
   function isArray(nodeName: String): Boolean {
    var numOfArrays: int = _arrays ? _arrays.length : 0;
    for (var i: int = 0; i < numOfArrays; i++) {
     if (nodeName == _arrays[i]) {
      return true;
     }
    }
    return false;
   }
   private static
   function getTypeof(o: * ): String {
    if (typeof(o) == "object") {
     if (o.length == null) {
      return "object";
     } else if (typeof(o.length) == "number") {
      return "array";
     } else {
      return "object";
     }
    } else {
     return typeof(o);
    }
   }
  }
 }

P.S.if you are wondering how to print out JSON in AS3 check out this article.

If you enjoy this post, share it on Twitter, Facebook or LinkedIn.