XML Format

Playin’ it Loose

XML is a rather strict syntax, but if you need something quick ’n’ dirty then use the 1.0 version. (See the first line)

Basic rules are:

  1. The first line describes the document. Since we are not specifying the tags and structure, ensure the version is 1.0 and standalone is "yes".
  2. The whole file must be wrapped by a tag pair (in this case, "Country_Data").
  3. Comments are the same as HTML comments.
  4. Attribute values must be enclosed in double-quotes (e.g. name="Americas")
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Country_Data>
<!-- If you have troubles with this XML file (for example, the site no longer works after this was edited)
    then try running this through an XML validation tool such as http://validator.w3.org/#validate_by_input    -->
<version>1.1</version>
<region name="Americas" sort="1" map="images/mapAmericas.png">
    <countries>
        <name>United States</name>
        <name>Canada, English speaking</name>
        <name>Canada, French speaking</name>
        <name>Brazil</name>
        <name>Peru</name>
    </countries>
    <graph>
        <xAxis origin="2.78" min="2.55" max="3.15" />
        <yAxis origin="2.91" min="2.85" max="3.1" />
    </graph>
</region>
    
<dataPoints>
    <country name="United States" x="2.76" y="2.94" gfx="flags/UnitedStates.png" />
    <country name="Canada, English speaking" x="2.79" y="2.95" gfx="flags/Canada.png" />
    <country name="Canada, French speaking" x="2.81" y="2.88" gfx="flags/Canada.png" />
    <country name="Norway" x="2.71" y="3.02" gfx="flags/Norway.png" />
    <country name="Finland" x="2.75" y="2.98" gfx="flags/Finland.png" />
    <country name="Sweden" x="2.79" y="2.93" gfx="flags/Sweden.png" />
</dataPoints>
</Country_Data>

Reading the XML File

I’ve been using jQuery and it works rather well once you get your head wrapped around the process.

Beer.Datafile = function(filename) {
     this.version;
     this.regions = [];
     this.dataPoints = [];
     this.countryList = [];
     this.loadData(filename);
     }
Beer.Datafile.prototype.loadData = function(filename) {
     jQuery.ajax({url: filename, context: this, dataType: 'xml', 
     error: function(jqXHR, textStatus, errorThrown) {
        alert('Could not load ' + filename + '\ntextStatus='+textStatus+'\nerrorThrown='+errorThrown+'\njqXHR='+jqXHR);
     },
     success: function(data){
        //This code executes asynchronously AFTER the xml file has succesfully been loaded.
        var self = this; //the variable assigned to Beer.Datafile
        
        //find returns jQuery object of matched elements
        $(data).find('version').each(function(index) {
            self.version = $(this).text();
        });
        
        $(data).find('Country_Data').children('region').each(function(index) { //Process the geography
            var regionObj = {
                name:$(this).attr("name"),
                "sort":Number($(this).attr("sort")),
                map:$(this).attr("map"),
                countries:[],
                graph:{}
            };
            $(this).children("countries").children("name").each(function(idx) {
                regionObj.countries.push($(this).text()); //country name
            });
            $(this).children("graph").children().each(function() {
                var axisName = this.nodeName;
                regionObj.graph[axisName] = {
                    origin:Number($(this).attr("origin")),
                    "min":Number($(this).attr("min")),
                    "max":Number($(this).attr("max"))
                }
            });
            self.regions.push(regionObj);
        });
        //Now that we have all the region/country info, sort the regions.
        self.regions = self.regions.sortBy(function(n) {
            return Number(n.sort); 
        });
        
        $(data).find('dataPoints').children().each(function(index) { //Process the (country) data points
            var dp = {
                country:$(this).attr("name"),
                x:Number($(this).attr("x")),
                y:Number($(this).attr("y")),
                gfx:$(this).attr("gfx")
            }
            self.dataPoints.push(dp);
        });
        //Build a list of countries based on the Data Points
        for (var cntry=0;cntry<self.dataPoints.length;cntry++) {
            self.countryList.push(self.dataPoints[cntry].country);
        }
        //Make a hash table of data points by Country name
        for (var i=0; i < self.dataPoints.length; i++) {
            self.dataPoints[self.dataPoints[i].country] = self.dataPoints[i];
        }
        initPage(); //Now that the file has been processed, begin the rest of the page scripts.
        }
    });
}

I found this comment on a blog talking about jQuery Deferreds. Looks like I may have to change the above code.

One of my big gripes with jQuery code that I see in the wild is that many people are not using the promise object methods when dealing with AJAX requests. Instead they are using the old success(), error() and complete(). jQuery has said in their docs that when 1.8 is released, those methods will be removed and you will then need to use the done(), fail() and always() correctly.