2

I want to create a Google Map dynamicly by loading data per AJAX. Therefore I use a JSON object with the identically structure of the GM API to build the map and jQuery to load per AJAX.

Like:

"object": {
    "div": "map1",
    "options": {
        "center": {
            "latitude": 38.608202,
            "longitude": 26.373749,
            "noWrap": false
        },
        "mapTypeId": "roadmap",
        "zoom": 7
    }
}

Will it be now a good idea to have also raw JavaScript in the JSON and parse it with the eval function like:

"object": {
    "div": "map1",
    "options": {
        "center": new google.maps.LatLng( 38.608202, 26.373749 ),
        "mapTypeId": google.maps.MapTypeId.ROADMAP,
        "zoom": 7
    }
}

With the default JSON parser of jQuery I get an error. But if I create my own converter with the eval function, it works.

jQuery.ajax({
    url: this.url,
    data: this.parameters,
    type: method,
    dataType: 'json',
    converters: {'text json': function( data ){
        eval( 'data = ' + data );
        return data;
    }},
    […]
});

I know this will not be a correct JSON format. But will I get some other troubles whith this, or is there any speed loss cause the eval function?

user2513437
  • 155
  • 10
  • Is it so hard for the AJAX server to create proper objects? – Barmar Jun 30 '13 at 09:54
  • It's not cause the AJAX server. In the first case I have to create every (!) GM object after loading. In the second it happens automaticly. And it will be easier to create the raw JavaScript on the Server. – user2513437 Jun 30 '13 at 10:37

1 Answers1

0

Will it be now a good idea to have also raw JavaScript in the JSON and parse it with the eval

No. Partly because of eval should be avoided, but more importantly because you're watering down the separation of concerns.

JSON is data. Data is not meant to be code. Your code should initialize its state with this data. It would be a nicer approach to refine your data so your code can be smarter about the way it does its initializing.

BTW performance is none of your issues here, so it should not be one of your concerns. Deal with optimizing performance when it is noticeably slow after it is good, not before.

How about

"object": {
    "div": "map1",
    "options": {
        "center": {
            "gmType": "LatLng",
            "init": [38.608202, 26.373749]
        },
        "mapTypeId": {
            "gmType": "MapTypeId",
            "init": "ROADMAP"
        },
        "zoom": 7
    }
}

and

// function from http://stackoverflow.com/a/3362623/
function construct(Constructor, args) {
    var Temp = function(){}, inst, ret;
    Temp.prototype = Constructor.prototype;
    inst = new Temp;
    ret = Constructor.apply(inst, args);
    return Object(ret) === ret ? ret : inst;
}

function jsonPrepare(jsonObject) {
    if ($.isPlainObject(jsonObject)) {
        $.each(jsonObject, function (key, item) {
            var target = null;
            if (item.hasOwnProperty("gmType")) {
                // initialize the GMaps object at this position
                target = google.maps[item.gmType];
                if ($.isFunction(target)) {
                    // ...either it is a constructor (like LatLng)
                    jsonObject[key] = construct(target, item.init);
                } else if (item.init in target) {
                    // ...or it is a look-up object (like MapTypeId)
                    jsonObject[key] = target[item.init];
                }
            } else {
                // recurse
                jsonPrepare(item);
            }
        });
    } else if ($.isArray(jsonObject)) {
        $.each(jsonObject, function (key, item) {
            // recurse
            jsonPrepare(item);
        });
    }
    return jsonObject;
}

and

jQuery.ajax({
    url: this.url,
    data: this.parameters,
    type: method,
    dataType: 'json'
}).then(jsonPrepare).then(function (data) {
    // your JSON object is now set up
}); 

This works because jsonPrepare modifies the object instance in-place.

More importantly you are now able to flexibly and declaratively approach object creation in your client - without resorting to mixing code and data.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • This looks like a solution and I tried it. But now I have a problem with the google.maps constructors: `target = google.maps['LatLng']; object = Object.create(target.prototype); target.apply( object, [48.608202,16.373749,false] );` will return undefined. – user2513437 Jun 30 '13 at 12:56
  • See modified answer. I've incorporated the generic `Constructor.apply()` solution from http://stackoverflow.com/a/3362623/. I'm a bit wary of using `Object.create` as this won't work on as many platforms. – Tomalak Jun 30 '13 at 13:29
  • You can extend that scheme to work with other object "initializers" in your JSON - the key point is that this approach is strictly white-listed and `eval()` is not. – Tomalak Jun 30 '13 at 13:44
  • eval is evil, I understand :) – user2513437 Jun 30 '13 at 13:53
  • No, not necessarily. It's just not a good practice. If you can make sure your data will never be maliciously modified you can `eval()` as much you like. The main difference here is that you're sending "typed" data that can be interpreted by a dynamic function, thus separating data and code. If Google changes their maps API, all you need to change is the interpeter code - your server can keep sending the same format. – Tomalak Jun 30 '13 at 13:58