Jump to content

a basic JSON question


Recommended Posts

I have a situation that I cannot figure out the answer to, yet I know it is probably straight forward.

I can send off a JSON set with data - but I would like to know how can I parse it back into the two records?

 

I don't follow how I can access the two records inside the data inside procedure HowDoIParseBack

TMyRecord1 = record
  caption : string;
  x : integer;
end;

TMyRecord2 = record
  SomeText : string;
end;

r1 : TMyRecord1;
r2 : TMyRecord1;

Procedure SendJSONToServer;
Begin
 var MyJSON := JSON.stringify(r1);
 MyJSON := MyJSON +','+JSON.stringify(r2);

 FHttp.Send(MyJSON);
end;

Procedure HowDoIParseBack;
begin
        var JSONStr:= fhttp.ResponseText;
        asm
         @r1 = JSON.parse(@JSONStr);
        end;
end;


 

Link to post
Share on other sites
  • Moderators

if you include ECMA.Json then you don't need to asm block your parse statements

don't know what your TR1 variable is, but define it as a variant. Do a

    console.log(@TR1);
//  {"business":"lynkfs","departments":[{"deptname":"marketing","staff":[{"first

and after parsing you can access your fields as in TR1.business and TR1.departments[0].deptname etc

.

Below is what I call my dogs breakfast. Just a set of steps to convert from one data structure to another. Multiple stringify's and parse's in there

  • step 1 : create a record structure 
  • step2 : change some values programmatically
  • step3 : change record structure to json
  • step4: create a TDataSet from json
  • step5 : save json to local storage
  • step 6: retrieve localstorage
  • step 7: create a SQLite db from json
  • step 8: save complete SQLite db to local storage and retrieve it again
  • step 9: save json to a MYSql db on server
  • step 10: and retrieve it again
unit Form1;

interface

uses
  System.Types,
  System.Types.Convert,
  System.Objects,
  System.Time,
  SmartCL.System,
  SmartCL.Time,
  SmartCL.Graphics,
  SmartCL.Components,
  SmartCL.FileUtils,
  SmartCL.Forms,
  SmartCL.Fonts,
  SmartCL.Theme,
  SmartCL.Borders,
  SmartCL.Application, SmartCL.Controls.Button, ECMA.JSON, System.DataSet,
  SmartCL.Storage.Local, System.Sqlite, W3C.XMLHttpRequest, W3C.HTML5, W3C.DOM,
  System.Streams, W3C.Console, system.structure, system.structure.json, system.json;

type
  TForm1 = class(TW3Form)
    procedure W3Button1Click(Sender: TObject);
  private
    {$I 'Form1:intf'}
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure Resize; override;
    Procedure DoStuff;
  end;

// record types
type
  JStrC = class(TJSonStructure)   //record
    firstname    : string;              //external 'firstname';
    surname      : string;              //external 'surname';
  end;

  JStrB = class(TJSonStructure)   //record
    deptname     : string;              //external 'deptname';
    staff        : Array of JStrC;      //external 'staff';
  end;

  JStrA = class(TJSonStructure)   //record
    business     : string;              //external 'business'
    departments  : Array of JStrB;      //external 'departments';
  end;

var document external 'document': variant;
var window   external 'window':   variant;
var console  external 'console':  variant;

implementation

{ TForm1 }


Procedure TForm1.DoStuff;
begin
//
// step1 : set up a leveled structure, (either records or classes)
//         i.e. business unit with departments with employees per department
    writeln('Step1');
//

/*
  //business unit
  var StrA01 : JStrA;
  StrA01.business    := 'lynkfs';

    //department marketing
    var StrB01 : JStrB;
    StrB01.deptname    := 'marketing';
      //staff Keith Ansell
      var StrC01 : JStrC;
      StrC01.firstname   := 'Claire';
      StrC01.surname     := 'Adams';
    StrB01.staff.Add(StrC01);
      //staff Charles Bailey
      var StrC02 : JStrC;
      StrC02.firstname   := 'Vicky';
      StrC02.surname     := 'Anderson';
    StrB01.staff.Add(StrC02);

  StrA01.departments.Add(StrB01);

    //department frontdesk
    var StrB02 : JStrB;
    StrB02.deptname    := 'front desk';
      //staff Keith Ansell
      var StrC03 : JStrC;
      StrC03.firstname   := 'Keith';
      StrC03.surname     := 'Ansell';
    StrB02.staff.Add(StrC03);
      //staff Charles Bailey
      var StrC04 : JStrC;
      StrC04.firstname   := 'Charles';
      StrC04.surname     := 'Bailey';
    StrB02.staff.Add(StrC04);

  StrA01.departments.Add(StrB02);
*/

  //business unit
  var StrA01 := JStrA.Create;
  StrA01.business    := 'lynkfs';

    //department marketing
    var StrB01 := JStrB.Create;
    StrB01.deptname    := 'marketing';
      //staff Claire Adams
      var StrC01 := JStrC.Create;
      StrC01.firstname   := 'Claire';
      StrC01.surname     := 'Adams';
    StrB01.staff.Add(StrC01);
      //staff Vicky Anderson
      var StrC02 := JStrC.Create;
      StrC02.firstname   := 'Vicky';
      StrC02.surname     := 'Anderson';
    StrB01.staff.Add(StrC02);

  StrA01.departments.Add(StrB01);

    //department frontdesk
    var StrB02 := JStrB.Create;
    StrB02.deptname    := 'front desk';
      //staff Keith Ansell
      var StrC03 := JStrC.Create;
      StrC03.firstname   := 'Keith';
      StrC03.surname     := 'Ansell';
    StrB02.staff.Add(StrC03);
      //staff Charles Bailey
      var StrC04 := JStrC.Create;
      StrC04.firstname   := 'Charles';
      StrC04.surname     := 'Bailey';
    StrB02.staff.Add(StrC04);

  StrA01.departments.Add(StrB02);

  // check
  writeln(inttostr(StrA01.departments.length) + ' departments');
  for var i := 0 to StrA01.departments.length -1 do begin
    writeln(StrA01.departments[i].deptname + ' ' + inttostr(StrA01.departments[i].staff.length) + ' staff');
    for var j := 0 to StrA01.departments[i].staff.length -1 do begin
      writeln(StrA01.departments[i].staff[j].firstname + ' ' +
              StrA01.departments[i].staff[j].surname);
    end;
  end;

  var jsontext: string := '';

  asm
    @jsontext = JSON.stringify(@StrA01);
    console.log(@jsontext);
//  {"business":"lynkfs","departments":[{"deptname":"marketing","staff":[{"firstname":"Vicky","surname":"Anderson"}]},{"deptname":"front desk","staff":[{"firstname":"Keith","surname":"Ansell"},{"firstname":"Charles","surname":"Bailey"}]},{"deptname":"new department","staff":[]}]}
  end;

  StrA01.Write('business','lynkfs');
  window.alert(StrA01.ToJson);

//
// step2 : depending on business logic, add/delete/change data in these arrays at will
    writeln('Step2');
//

  // employee Claire Adams leaves
  StrA01.departments[0].staff.Delete(0);    //or coding to that effect

  // a new empty department is created
  var StrB := JStrB.Create;
  StrB.deptname := 'new department';
  StrA01.departments.add(StrB);

  // check
  writeln(inttostr(StrA01.departments.length) + ' departments');
  for var i := 0 to StrA01.departments.length -1 do begin
    writeln(StrA01.departments[i].deptname + ' ' + inttostr(StrA01.departments[i].staff.length) + ' staff');
    for var j := 0 to StrA01.departments[i].staff.length -1 do begin
      writeln(StrA01.departments[i].staff[j].firstname + ' ' +
              StrA01.departments[i].staff[j].surname);
    end;
  end;

//
// step3 : Turn object to json
    writeln('Step3');
//
  var textdata: string := '';

  asm
    @textdata = JSON.stringify(@StrA01);
    console.log(@textdata);
//  {"business":"lynkfs","departments":[{"deptname":"marketing","staff":[{"firstname":"Vicky","surname":"Anderson"}]},{"deptname":"front desk","staff":[{"firstname":"Keith","surname":"Ansell"},{"firstname":"Charles","surname":"Bailey"}]},{"deptname":"new department","staff":[]}]}
  end;

//
// step4 : Define and populate TW3DataSet from json
    writeln('Step4');
//
  var DataSet := TW3DataSet.Create;      // note TW3Dataset is not leveled

  Dataset.FieldDefs.Add('business',ftString);
  Dataset.FieldDefs.Add('deptname',ftString);
  Dataset.FieldDefs.Add('firstname',ftString);
  Dataset.FieldDefs.Add('surname',ftString);

  DataSet.CreateDataset;

  var v := json.parse(textdata);

  For var i := 0 to v.departments.length -1 do begin
    For var j := 0 to v.departments[i].staff.length -1 do begin
      DataSet.Append;
      Dataset.Fields.FieldByName('business').AsString := v.business;
      Dataset.Fields.FieldByName('deptname').AsString := v.departments[i].deptname;
      Dataset.Fields.FieldByName('firstname').AsString := v.departments[i].staff[j].firstname;
      Dataset.Fields.FieldByName('surname').AsString   := v.departments[i].staff[j].surname;
      DataSet.Post;
    end;
    If v.departments[i].staff.length = 0 then begin     //empty department
      DataSet.Append;
      Dataset.Fields.FieldByName('business').AsString := v.business;
      Dataset.Fields.FieldByName('deptname').AsString := v.departments[i].deptname;
      DataSet.Post;
    end;
  end;

  // check
  DataSet.First;
  while not DataSet.Eof do
  begin
    writeln(inttostr(DataSet.recno+1) + ' ' +
            Dataset.Fields.FieldByName('business').AsString + ' ' +
            Dataset.Fields.FieldByName('deptname').AsString + ' ' +
            Dataset.Fields.FieldByName('firstname').AsString + ' ' +
            Dataset.Fields.FieldByName('surname').AsString);
    DataSet.Next;
  end;

//
// step5 : save json to local storage (persistent on specific device)
    writeln('Step5');     // step 5 only needs to be run once
//
  var LocalStorage := TW3LocalStorage.Create;

  LocalStorage.Open('mytreasure');
  LocalStorage.SetKeyStr('json',textdata);
  LocalStorage.Close;

//bypassing SmartCL.Storage.Local :
  window.localStorage.setItem('pete',textdata);

//
// step6 : retrieve json from local storage (persistent on specific device)
    writeln('Step6');
//
  textdata := '';

  LocalStorage.Open('mytreasure');
  textdata := LocalStorage.GetKeyStr('json','unknown');
  LocalStorage.Close;

  var v1 := json.parse(textdata);

  // check
  writeln(inttostr(v1.departments.length) + ' departments');
  for var i := 0 to v1.departments.length -1 do begin
    writeln(v1.departments[i].deptname + ' ' + inttostr(v1.departments[i].staff.length) + ' staff');
    for var j := 0 to v1.departments[i].staff.length -1 do begin
      writeln(v1.departments[i].staff[j].firstname + ' ' +
              v1.departments[i].staff[j].surname);
    end;
  end;

//Bypassing SmartCL.Storage.Local
  v1 := json.parse(window.localStorage.getItem('pete'));

  // check
  writeln('/////////////////////pete/////////////////////////////');
  writeln(inttostr(v1.departments.length) + ' departments');
  for var i := 0 to v1.departments.length -1 do begin
    writeln(v1.departments[i].deptname + ' ' + inttostr(v1.departments[i].staff.length) + ' staff');
    for var j := 0 to v1.departments[i].staff.length -1 do begin
      writeln(v1.departments[i].staff[j].firstname + ' ' +
              v1.departments[i].staff[j].surname);
    end;
  end;

//
// step7 : save json to SQLite
    writeln('Step7');
//

  var db   : TSQLiteDatabase;
  var res  : TSQLiteResult;

  db := TSQLiteDatabase.Create;
  db.Run('CREATE TABLE `business`    (id INTEGER, business TEXT)');
  db.Run('CREATE TABLE `departments` (id INTEGER, busid INTEGER, deptname TEXT)');
  db.Run('CREATE TABLE `staff`       (id INTEGER, deptid INTEGER, busid INTEGER, firstname TEXT, surname TEXT)');

  var v2 := json.parse(textdata);

  db.Run("INSERT INTO `business`(`id`,`business`) VALUES (0,'" + v2.business + "')");
  for var i := 0 to v2.departments.length -1 do begin
    db.Run("INSERT INTO `departments`(`id`,`busid`,`deptname`) VALUES (" +
         inttostr(i) + ",0,'" + v2.departments[i].deptname + "')");
    for var j := 0 to v2.departments[i].staff.length -1 do begin
      db.Run("INSERT INTO `staff`(`id`,`deptid`,`busid`,`firstname`,`surname`) VALUES (" +
           inttostr(j) + "," + inttostr(i) + ",0,'" + v2.departments[i].staff[j].firstname +
           "','" + v2.departments[i].staff[j].surname + "')");
    end;
  end;

//  check
//  res := db.Exec("SELECT a.business, b.deptname, c.firstname, c.surname " +
//                 "from business a, departments b, staff c " +
//                 "where c.busid = a.id and c.deptid = b.id and b.busid = a.id");

// or something a bit simpler
  res := db.Exec("SELECT surname FROM staff");

  //writeln(res.columns[0]);   //title of first field : 'business'

  var rows := res.values;
  for var i := 0 to rows.length -1 do begin
    var columns := rows[i].values;
    for var j := 0 to columns.length-1 do begin
      writeln(columns[j]);
    end;
  end;

// or alternatively, same result
//
//  for var i := 0 to res.values.length -1 do begin                   //rows
//    for var j := 0 to res.values[i].values.length-1 do begin        //columns
//      writeln(res.values[i].values[j]);                             //cell
//    end;
//  end;

//
// step8 : save sqlite db to localstorage
    writeln('Step8');
//
  var MyStream : TStringStream;
  MyStream := TStringStream.Create;
  db.SaveToStream(MyStream,true);
  var textdata3 := MyStream.DataString;
  console.log(textdata3);

  //save textdata3 to local storage
  var LocalStorage2 := TW3LocalStorage.Create;

  LocalStorage2.Open('sqlite');
  LocalStorage2.SetKeyStr('stream',textdata3);
  LocalStorage2.Close;

  //read textdata3 from local storage
  LocalStorage2.Open('sqlite');
  textdata3 := LocalStorage2.GetKeyStr('stream','unknown');
  LocalStorage2.Close;

  //populate stream with localstorage data and revive sqlite db from stream
  MyStream.DataString := textdata3;
  MyStream.Position := 0;
  console.log(mystream.datastring);

  var db2 := TSQLiteDatabase.Create;
  db2.LoadFromStream(MyStream);
  writeln(db2.active);

  res := db2.Exec("SELECT surname FROM staff");

  //test
  for var i := 0 to res.values.length -1 do begin                   //rows
    for var j := 0 to res.values[i].values.length-1 do begin        //columns
      writeln(res.values[i].values[j]);                             //cell
    end;
  end;

/*
//
// step9 : save json to server (persistent over all devices)
    writeln('Step8');
//

  var s := "UPDATE `test` SET `webjson`='" + textdata + "'";
  var FHttp := JXMLHttpRequest.Create;
  FHttp.open("POST",'...../smsdmlmysql.php');
  FHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  var encodedstr1 := browserapi.window.encodeURIComponent(S);
  var sql_statement := 'sql_statement=' + encodedstr1;
  FHttp.send(sql_statement);

//
// step10 : retrieve json from server
    writeln('Step9');
//

  s := "select * from test";
  var FHttp2 := JXMLHttpRequest.Create;
  FHttp2.open("POST",'...../smsdbmysql.php');
  FHttp2.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  var encodedstr2 := browserapi.window.encodeURIComponent(S);
  var sql_statement2 := 'sql_statement=' + encodedstr2;
  FHttp2.send(sql_statement2);

  JGlobalEventHandlers(FHttp2).onLoad := lambda(e:JEvent)
    var smscursor := JSON.parse(FHttp2.responseText);
    for var i := 0 to smscursor.rows.length -1 do begin
      textdata := smscursor.rows[i].webjson;
    end;
    writeln(textdata);
    result :=true;
  end;
*/
end;

procedure TForm1.W3Button1Click(Sender: TObject);
begin
  DoStuff;
end;

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
end;

procedure TForm1.Resize;
begin
  inherited;
end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.

 

 

Link to post
Share on other sites

Sorry, my type Tr1 was supposed to be r1.

I haven't tried your code yet - but the main problem I was trying to solve is that I have two records in the JSON and I need to retrieve them into two different variables.

 

So if I put r1 and r2 (which are different record types) into single JSON how do I parse it back?

If your code answer the question then please ignore I will have a play when I have a few quiet moments.

Link to post
Share on other sites
  • Administrators

@Czar If you blindly concatenate json objects into string, you can't parse them back unless you make sure to separate the json objects from the string. So you need to have some way of knowing how long each json object is in the string and call json.parse on the substrings.

 

Link to post
Share on other sites
  • Administrators
1 hour ago, Czar said:

Thanks, however that is the bit that I am not understanding how to do. So I need to put the records into the JSON string differently first so I can get them out.

Yes. There are many ways you can do that. Here's a quick function that splits the string into a StrArray:

function SplitJson(JsonStr: String): TStrArray;
var i,Start,QCount: Integer;
    InQ: Boolean;
begin
  Start:=1;
  for i:=1 to Length(JsonStr) do begin
    var ch:=JsonStr[i];
    case ch of
      '"': begin
             if not InQ then InQ:=True
             else if JsonStr[i-1]<>'\' then InQ:=False;
           end;
      '{': begin
             if not InQ then inc(QCount);
           end;
      '}': begin
             if not InQ then dec(QCount);
             if QCount=0 then begin
               result.Add(copy(JsonStr,Start,i-Start+1));
               Start:=i+1;
             end;
           end;
    end;
  end;
  if i>0 then result.Add(copy(JsonStr,Start,i-Start+1));
end;

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...