user3434120
user3434120

Reputation: 27

Non-existing XML node breaking my script

Some of the XML nodes is not always present in the XML feed, like gate and status, causing my code to break. This is my code:

$( document ).ready(function() {
    $('#load_btn').click(function() {
        loadXMLDoc();
    });
});  

function loadXMLDoc() {

    $.ajax({
        url: "proxy.php",
        type: "POST",
        dataType: "xml",
        data: {
            address: "http://flydata.avinor.no/XmlFeed.asp?TimeFrom=1&TimeTo=7&airport=OSL&direction=D&lastUpdate=2016-04-12T15:03:00Z"
        },

    })

    .done(function (xml) {

        var flightXML = $(xml).find('flight');
        //console.log(flightXML);
        var output = "<table>";
        output += "<tr><th>Departue</th><th>Flight</th><th>To</th><th>Airline</th><th>Gate</th></tr>"
        for(i=0; i < flightXML.length; i++)
            {

                var line = "<tr>";
                var timeElement = flightXML[i].getElementsByTagName("schedule_time");
                var time = timeElement[0].firstChild.nodeValue;
                var idElement = flightXML[i].getElementsByTagName("flight_id");
                var id = idElement[0].firstChild.nodeValue;
                var toElement = flightXML[i].getElementsByTagName("airport");
                var to = toElement[0].firstChild.nodeValue;
                var airlineElement = flightXML[i].getElementsByTagName("airline");
                var airline = airlineElement[0].firstChild.nodeValue;
                var gateElement = flightXML[i].getElementsByTagName("gate");
                var gate = gateElement[0].firstChild.nodeValue;
                //var statusElement = flightXML[i].getElementsByTagName("status");
                //var status = statusElement[0].firstChild.nodeValue;  

                line += "<td>" + time + "</td><td>" + id + "</td><td>" + to + "</td><td>" + airline + "</td><td>" + gate + "</td>";
                line += "</tr>";
                output += line;
            }
        output += "</table>";
        document.getElementById("result").innerHTML = output;


    });
}

This is the XML structure:

<flight uniqueID="5819145">
  <airline>SK</airline>
  <flight_id>SK815</flight_id>
  <dom_int>I</dom_int>
  <schedule_time>2016-04-12T18:15:00Z</schedule_time>
  <arr_dep>D</arr_dep>
  <airport>LHR</airport>
  <check_in>7 8</check_in>
  <gate>F19</gate>
</flight>
<flight uniqueID="5818372">
  <airline>EW</airline>
  <flight_id>EW4197</flight_id>
  <dom_int>S</dom_int>
  <schedule_time>2016-04-12T18:15:00Z</schedule_time>
  <arr_dep>D</arr_dep>
  <airport>HAM</airport>
  <check_in>7</check_in>
  <status code="C"></status>
</flight>
<flight uniqueID="5818645">
  <airline>SK</airline>
  <flight_id>SK291</flight_id>
  <dom_int>D</dom_int>
  <schedule_time>2016-04-12T18:15:00Z</schedule_time>
  <arr_dep>D</arr_dep>
  <airport>BGO</airport>
  <check_in>7 8</check_in>
  <gate>A4</gate>
</flight>

How can I check for presence of these nodes, and if not present, insert a blank space (or whatever is needed for my code not to break)?

Upvotes: 0

Views: 58

Answers (2)

Tomalak
Tomalak

Reputation: 338316

You should separate your concerns better. Resist the temptation to write a function that does everything.

First concern: Request proxying

Let's abstract the plumbing for a proxied request into a function, complete with URL parameters that we can pass as an object.

function proxy(url, params) {
    if (!params || typeof params !== "object") params = {};
    return $.ajax({
        type: "post",
        url: "proxy.php",
        data: {address: url + "?" + $.param(params)}
    });
}

Remember to set the Content-Type header in proxy.php equal to the content type of the target page.

Second concern: Getting data from the remote API

We want an array of flights, where each flight has the properties schedule_time, flight_id, airport, airline, gate.

On top of that we want to pass in the query parameters (TimeFrom, TimeTo, airport, direction, lastUpdate) in a structured way.

function getFlights(params) {
    return proxy("http://flydata.avinor.no/XmlFeed.asp", params).then(function (xml) {
        return $(xml).find('flight').map(function () {
            var $flight = $(this);
            return {
                schedule_time: $flight.find("schedule_time").text(),
                flight_id: $flight.find("flight_id").text(),
                airport: $flight.find("airport").text(),
                airline: $flight.find("airline").text(),
                gate: $flight.find("gate").text()
            };
        }).toArray();
    });
}

Using .then() allows us to determine the overall result of the Ajax request, in our case it will be no longer XML but an array of uniform objects (no matter whether certain elements were missing in the XML).

Third concern: rendering

We want to turn an array of flights into a table. I would recommend handlebars.js for HTML generation, but we can do it manually with jQuery as well:

function renderFlights(flights) {
    var $table = $("<table>");
    $table.append("<tr><th>Departue</th><th>Flight</th><th>To</th><th>Airline</th><th>Gate</th></tr>");
    $.each(flights, function (i, flight) {
        var $tr = $("<tr>").appendTo($table);
        $("<td>", {text: flight.schedule_time }).appendTo($tr);
        $("<td>", {text: flight.flight_id }).appendTo($tr);
        $("<td>", {text: flight.airport }).appendTo($tr);
        $("<td>", {text: flight.airline }).appendTo($tr);
        $("<td>", {text: flight.gate }).appendTo($tr);
    });
    return $table;
}

Building HTML this way is safer than concatenating strings. It protects you against cross-site scripting attacks and random breakage due to unexpected special characters in the text.

Fourth concern: User interface wire-up

Now we can bind a button click very easily like this:

$(function() {
    $('#load_btn').click(function () {
        getFlights({
            TimeFrom: 1,
            TimeTo: 7,
            airport: "OSL",
            direction: "D",
            lastUpdate: "2016-04-12T15:03:00Z"
        }).done(function (flights) {
            var $table = renderFlights(flights);
            $("#result").empty().append($table);
        });
    });
});  

Upvotes: 1

Will
Will

Reputation: 3241

Depending on where it blows up, this might work:

var gateElement = flightXML[i].getElementsByTagName("gate");
var gate = gateElement[0].firstChild.nodeValue || "Gate not available";

I can't remember offhand if that would work. Might have to do:

var gate = "Gate not available";
var gateElement = flightXML[i].getElementsByTagName("gate");
if(gateElement) gate = gateElement[0].firstChild.nodeValue;

Upvotes: 0

Related Questions