18.10.2013

Recent additions to delphi-xe-json

I finally found some time to add two features to my delphi-xe-json library.

I wrote a Formatter for JSON, that doesn't try to read the input into an IJSONArray or IJSONObject. The advantage is, that it even formats invalid JSON, which might make it easier to spot what is wrong with it.
I'm not completely happy with it, because it violates the DRY-Principle twice. I copied a method from TJSONReader and another one from TJSONReadableWriter, but I already have an idea how to fix that.

The other addition is bigger and still a work in progress, which is why I haven't merged the branch back into the master branch. I decided to share it anyway, because it is working with some restrictions and I don't think that the interface is changing a lot in the next time.

The idea was to generate Plain Old Delphi Objects (PODOs) from a JSON string. I utilized the code generator from Jeroen Pluimers which he kindly put under BSD-License in the repository from better-office benelux.

For convenience, I copied the three needed units in the delphi-xe-json repository, but I recommend using the original ones.

The usage is very simple:
var
  jo : IJSONObject;
begin
  jo := TJSON.NewObject({some json string});
  Memo1.Lines.Text := TJSON.GeneratePODOUnit(jo);
end;

JSON-Input:
{
  "Name":"Test",
  "AnswerToLife":42,
  "Items":
  [
    1.41,
    2.72,
    3.13
  ]
}

Generated Unit
unit GeneratedUnit;

interface

uses
  Generics.Collections,
  JSON;

type
  TItemsList = class;
  TGeneratedClass = class;

  TItemsList = class(TList<double>)
  public
  procedure LoadFromFile(aFilename : string);
  procedure SaveToFile(aFilename : string);
  procedure LoadFromJSON(ja : IJSONArray);
  procedure SaveToJSON(ja : IJSONArray);
  end;

  TGeneratedClass = class(TObject)
  strict private
  fItems: TItemsList;
  fAnswerToLife: integer;
  fName: string;
  public
  procedure LoadFromFile(aFilename : string);
  procedure SaveToFile(aFilename : string);
  constructor Create();
  destructor Destroy(); override;
  procedure LoadFromJSON(jo : IJSONObject);
  procedure SaveToJSON(jo : IJSONObject);
  property Items: TItemsList read fItems;
  property AnswerToLife: integer read fAnswerToLife write fAnswerToLife;
  property Name: string read fName write fName;
  end;

implementation

uses
  IOUtils;

{ TItemsList }

procedure TItemsList.LoadFromFile(aFilename : string);
var
  json: IJSONArray;
  s: string;
begin
  s := TFile.ReadAllText(aFilename);
  json := TJSON.NewArray(s);
  LoadFromJSON(json);
end;

procedure TItemsList.SaveToFile(aFilename : string);
var
  json: IJSONArray;
begin
  json := TJSON.NewArray;
  SaveToJSON(json);
  TFile.WriteAllText(aFilename, json.ToString);
end;

procedure TItemsList.LoadFromJSON(ja : IJSONArray);
var
  i: integer;
begin
  for i := 0 to ja.Count - 1 do
  begin
    Items[i] := ja.GetDouble(i);
  end;
end;

procedure TItemsList.SaveToJSON(ja : IJSONArray);
var
  i: integer;
begin
  for i := 0 to ja.Count - 1 do
  begin
    ja.put(Items[i]);
  end;
end;

{ TGeneratedClass }

procedure TGeneratedClass.LoadFromFile(aFilename : string);
var
  json: IJSONObject;
  s: string;
begin
  s := TFile.ReadAllText(aFilename);
  json := TJSON.NewObject(s);
  LoadFromJSON(json);
end;

procedure TGeneratedClass.SaveToFile(aFilename : string);
var
  json: IJSONObject;
begin
  json := TJSON.NewObject;
  SaveToJSON(json);
  TFile.WriteAllText(aFilename, json.ToString);
end;

constructor TGeneratedClass.Create();
begin
  fItems := TItemsList.Create;
end;

destructor TGeneratedClass.Destroy();
begin
  fItems.Free;
  inherited;
end;

procedure TGeneratedClass.LoadFromJSON(jo : IJSONObject);
begin
  fItems.LoadFromJSON(jo.GetJSONArray('Items'));
  fAnswerToLife := jo.GetInteger('AnswerToLife');
  fName := jo.GetString('Name');
end;

procedure TGeneratedClass.SaveToJSON(jo : IJSONObject);
var
  jaItems: IJSONArray;
begin
  jaItems := TJSON.NewArray;
  fItems.SaveToJSON(jaItems);
  jo.Put('Items', jaItems);
  jo.Put('AnswerToLife', fAnswerToLife);
  jo.Put('Name', fName);
end;

end. 

1 Kommentar:

Jeroen Pluimers hat gesagt…

I'm updating my codegenerator. It is now at https://bitbucket.org/jeroenp/besharp.net/src/tip/Native/Delphi/Library/RTL/TypeInformation/CodeGeneratorUnit.pas

--jeroen