Brought to you by LEAP™ 5 and Lasso 9

Making JSON Simple

JSON is the communications backbone of the modern data-responsive web. It is actually quite simple - and yet I've been surprised how many devs are daunted by it.

So lets demystify it and make it simple :)

First, lets get out of the way the two most common questions I get about JSON:

What is JSON?

JSON stands for JavaScript Object Notation. It does much of the same job as we used to use XML for, but where XML was verbose and the standards varied from one implementation to another (yes, I'm talking about you Micrososft), JSON is human readable, simple, efficient.

No, really, why is JSON so cool?

If you "get" the concept of arrays and maps, then JSON is just that. Arrays and maps (i.e. pairs of name/value data). Nothing more, nothing less.

Also, JSON is stuff the server understands - whether you're using Lasso as the backend, or PHP, or Node.js... and yet because it's JavaScript, your browser just uses it, no fancy interpreter, no complicated dark arts trickery.

JSON, server side

There's two main ways you want to use JSON server side - as data incoming, and data outgoing.

Incoming data

Incoming data in JSON format is usually going to be something you're fetching as JSON from an external API service like Flickr, weather services or a shipping company.

For the purposes of this article I am going to use the Lasso 9 reference REST API.

If we wanted to include the results of a search for the keyword "cookie" on our page, we would first fetch it, then convert it to a native data type.

local(
   result = include_url(
      'http://api.lassoref.com/lasso9/LassoReferenceApi/search.xhr?q=cookie'
   )
)
local(deserialized = json_deserialize(#result))
#deserialized

The JSON returned (truncated at one result by me):

{
   "error":"No Error",
   "queries":[
      "random",
      "cookie",
      "findposition"
   ],
   "results":[
      {
         "category":60,
         "categoryname":"Cookie",
         "datatype":"m",
         "datatypeext":"Method",
         "description":"[Cookie] returns the value for a named cookie....",
         "fullname":"cookie",
         "id":1796,
         "name":"cookie",
         "parentis":""
      },
      {
         "category":60,
         "categoryname":"Cookie",
         "datatype":"m",
         "datatypeext":"Method",
         "description":"[Cookie_Set] sets a cookie with a given name and...",
         "fullname":"cookie_set",
         "id":1797,
         "name":"cookie_set",
         "parentis":""
      }
   ]
}

So you can see here that we have 3 "top level" elements: 'error', 'queries' and 'results'.

'Error' is a simple string. 'Queries' and 'Results' are arrays - and in fact each element in 'Results' is a map (name / value pairs).

The next step server side, is using it.

#deserialized->find('error')
/* Output: No Error */

#deserialized->find('queries')
/* Output: array('random','cookie','findposition') */

#deserialized->find('results')->get(1)->find('description')
/* Output: [Cookie] returns the value for a named cookie.... */

Generating JSON output to send to the browser

This is the biggest area of misunderstanding. "But how do I generate all that code?"

Step 1, assemble the components you want to send back.

In the above example, we're sending back 3 objects, a string, an array of strings, and an array of maps.

Step 2, make a map of the data to return.

Step 3, serialize it (and output).

The code below assembles the variables, and then in one master stroke we convert to JSON and output.

// First, we force the returned mime type to be json
content_type('application/json')
// for a better drop-in replacement see the end of the article

// set the local vars to what we want
local(queries = array('random','cookie','findposition'))
local(results = array(
   map(
      'category' = 60, 
      'categoryname' = 'Cookie', 
      'datatype' = 'm', 
      'datatypeext' = 'Method', 
      'description' = '[Cookie] returns the value for a named cookie….', 
      'fullname' = 'cookie', 
      'id' = 1796, 
      'name' = 'cookie', 
      'parentis' = ''
      )
   )
)
local(toreturn = map(
      'error' = 'No error',
      'queries' = #queries,
      'results' = #results
   )
)

// now in one movement serialize (convert) to JSON and output
json_serialize(#toreturn)

What you get delivered to your browser is functionally identical to that which we're getting from the API.

The special case: storing data as JSON

Some db storage engines have begun to implement special storage types for JSON data. The advantages of having special JSON storage types have yet to hit me fully but it's likely going to be the same as what I do with MySQL & Postgres now anyway.

To do that, simply assemble your map/array data and create your JSON using json_serialize. Save to column. Done.

Using JSON in the browser

Lets look at running the same query, but in the browser via a function call.

We're going to use the jQuery AJAX method to first fetch the data, then on successful retrieve, display to the user.

function searchme(q){
   $.ajax({
      url: 'http://api.lassoref.com/lasso9/LassoReferenceApi/search.xhr?q='+q,
      async: true,
      type: 'get',
      dataType: 'jsonp',
      success: function(data) {
         $('#errordisplay').html(data.error);
         $('#descriptiondisplay').html(data.results[0].description);
      }
   });
};
$(document).ready(function() {
   searchme('cookie');
});

It's not hard really is it?

Questions, just email me or use the comments below...

Update:

It's been pointed out to me by Steffan Cline that I was oversimplifying the content type header.

Below is what I normally use to set the header, because MSIE does things different to the rest of the universe.

The method can be found at LassoSoft's community repository: http://www.lassosoft.com/tagswap/detail/json_header

/* ==================================
	Sets headers for JSON content
	provides the fix for MSIE not handling JSON content types correctly
	- Thanks to Steffan Cline for pointing this out
================================== */

define json_header() => {
   if(web_request->httpUserAgent >> 'MSIE') => {
      web_response->replaceHeader(
         'Content-Type'='text/javascript; charset=' + http_document->encoding
      )
   else
      web_response->replaceHeader(
         'Content-Type'='application/json; charset=' + http_document->encoding
      )
   }
}

// usage is as simple as
json_header
// instead of using content_type('application/json')

Relevant Links

Wikipedia on JSON: http://en.wikipedia.org/wiki/Json

Lasso 9 Reference, json_deserialize: http://www.lassosoft.com/LassoDocs/languageReference/obj/json_deserialize

Lasso 9 Reference, json_serialize: http://www.lassosoft.com/LassoDocs/languageReference/obj/json_serialize

Lasso 9 Reference REST API Docs: http://api.lassoref.com/lasso9/LassoReferenceApi/docs

jQuery AJAX: http://api.jquery.com/jQuery.ajax/

JSON Formatter & Validator: http://jsonformatter.curiousconcept.com/#jsonformatter

json_header: http://www.lassosoft.com/tagswap/detail/json_header

comments powered by Disqus