sábado, 12 de novembro de 2016

Static JSON in Smart Mobile Studio

A tidy solution for using static JSON data in Smart Mobile Studio

I wanted to use JSON to store some some static data. I could have a separate .js file that holds my JSON data as an object - but this seems messy.

Since most browsers support JSON.parse(), which is defined in ECMA-JSON unit, we can declare any JSON string in SmartMS, and you can always use the JSON.parse method to get the JSON object.

AJAX seems a bit overkill - it just needs to evaluate it at run-time, nothing is dynamic. You can declare this JSON string in global constants or variables. In this experiment I will declare in the class scope using it in a private field, class const, class var and in a const

type
  TForm1 = class(TW3Form)
  private
    { private declarations }
    OBJ_CLASS: variant    = '{"employees":[{"id":1,"firstName":"smart","lastName":"mobilestudio"}]}';
    class Const OBJ_CONST = '{"employees":[{"id":1,"firstName":"smart","lastName":"mobilestudio"}]}';
    class var OBJ_VAR     = '{"employees":[{"id":1,"firstName":"smart","lastName":"mobilestudio"}]}';
    Const OBJCONSTANT     = '{"employees":[{"id":1,"firstName":"smart","lastName":"mobilestudio"}]}';
  protected
    { protected declarations }
  end;

implementation

Const GLOBALOBJCONST      = '{"employees":[{"id":1,"firstName":"smart","lastName":"mobilestudio"}]}';
var GLOBALOBJVAR: variant = '{"employees":[{"id":1,"firstName":"smart","lastName":"mobilestudio"}]}';

The functionally class vars in Smart Pascal work like class-scoped global variables, i.e. their lifetime is global and there is only one copy of the variable per declaration. Indeed, before having access to proper class variables, most Delphi programmers would use a global variable hidden in the implementation section of the unit that declares the class instead. 

  {* Using private Class var *}
  var obj3 := JSON.Parse(OBJ_VAR);
  WriteLn(obj3.employees[0]);

  var obj4 := JSON.Parse(OBJ_VAR);
  WriteLn(obj4.employees[0]);

  {*----- Using global var ---------*}
  var obj7 := JSON.Parse(GLOBALOBJVAR);
  WriteLn(obj7.employees[0]);

  var obj8 := JSON.Parse(GLOBALOBJVAR);
  WriteLn(obj8.employees[0]);

This is recommended way, there's just one copy the JSON string through the main js file.

{ main.js }
obj3 = JSON.parse(OBJ_VAR);
WriteLn(obj3.employees[0]);
obj4 = JSON.parse(OBJ_VAR);
WriteLn(obj4.employees[0]);

obj7 = JSON.parse(GLOBALOBJVAR);
WriteLn(obj7.employees[0]);
obj8 = JSON.parse(GLOBALOBJVAR);
WriteLn(obj8.employees[0]);

{ global application scope }
var OBJ_VAR = "{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}";
var GLOBALOBJVAR = "{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}";

Class const - a constant value is associated with the class itself and not an instance of the class, this will also work like global const but you can have more than one copy of the JSON string per declaration.

For instance. let say you declare several objects that use the same JSON object, and you have declared the JSON string in a const or class const

  {*--- Using private Class Const ---*}
  var obj1 := JSON.Parse(OBJ_CONST);
  WriteLn(obj1.employees[0]);

  var obj2 := JSON.Parse(OBJ_CONST);
  WriteLn(obj2.employees[0]);

  {*----- Using global Const ----------*}
  var obj5 := JSON.Parse(GLOBALOBJCONST);
  WriteLn(obj5.employees[0]);

  var obj6 := JSON.Parse(GLOBALOBJCONST);
  WriteLn(obj6.employees[0]);

  {*----- Using private Const ---------*}
  var obj11 := JSON.Parse(Self.OBJCONSTANT);
  WriteLn(obj11.employees[0]);

  var obj12 := JSON.Parse(Self.OBJCONSTANT);
  WriteLn(obj12.employees[0]);

There is is a drawback, a lot of repetitive JSON string will be emitted in the main JS file.

{ main.js }
  obj1 = JSON.parse("{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}");
  WriteLn(obj1.employees[0]);
  obj2 = JSON.parse("{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}");
  WriteLn(obj2.employees[0]);

  obj5 = JSON.parse("{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}");
  WriteLn(obj5.employees[0]);
  obj6 = JSON.parse("{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}");
  WriteLn(obj6.employees[0]);

  obj11 = JSON.parse("{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}");
  WriteLn(obj11.employees[0]);
  obj12 = JSON.parse("{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}");
  WriteLn(obj12.employees[0]);   

If we store the JSON string in a private field, there's one copy per declaration as well, we get the expected result, the json string will be stored in class scope.

var TForm1 = {
   $ClassName:"TForm1",$Parent:TW3Form
   ,$Init:function ($) {
      TW3Form.$Init($);
      $.OBJ_CLASS = "{\"employees\":[{\"id\":1,\"firstName\":\"smart\",\"lastName\":\"mobilestudio\"}]}";
   }
   /// procedure TForm1.InitializeForm()
   ,InitializeForm:function(Self) {
      TW3CustomForm.InitializeForm(Self);
   }
   /// procedure TForm1.InitializeObject()
   ,InitializeObject:function(Self) { ...} 


JS resource approach

You can kind-of simulate by using global variables (all derived classes, units and forms will share the same variable) using the JS resource approach. Under Project Manager, right click at Resources, choose New... Javascript.Just declare some variable (the resource name for instance) to store some raw javascript as the JSON object:

var JS1 = {
 "employees" : [{
   "id" : 1,
   "firstName" : "smart",
   "lastName" : "mobilestudio"
  }]}

Under the implementation session, declare an external property such as:

function data: variant; external 'JS1' property;

IMHO, this would be the most natural syntax I came across, I'm accessing the pure JSON object :)

  var obj13 := data.employees[0];
  WriteLn(obj13);

  var obj14 := data.employees[0];
  WriteLn(obj14);