How to get JSON, which contains only Name and Flags?
TUser = class
private
FName: string;
FFlags: UInt32;
FFields: UInt32;
FCreated: TDateTime;
public
property Name: string read FName write FName;
property Flags: UInt32 read FFlags write FFlags;
property Fields: UInt32 read FFields write FFields;
property Created: TDateTime read FCreated write FCreated;
constructor Create;
end;
Usually is used all fields of this class:
var
User: TUser
sJson: string;
sJson := User.AsJson;
but sometimes I needed a JSON with Name and Flags fields only.
Currently, to get such JSON I use such code:
var
User: TUser
usr: ISuperObject;
sJson: string;
usr := SO(User.AsJson);
usr.Remove('Fields');
usr.Remove('Created');
sJson := usr.AsJSON;
But I think is not optimal code (actually in real code I have 15 fields and need to remove 12). How to do that faster?
Updated (another method):
May be this will be more faster and useful for my purpose?
usr := SO('');
usr.S['Name'] := User.Name;
usr.I['Flags'] := User.Flags;
sJson := usr.AsJSON;
Thanks to the #NasreddineGalfout I'm found that is it possible with Neon JSON library. With INeonConfiguration I can select public or published or protected (or any combination) property fields that should be serialized. And is it what I need. Moreover deserializing with Neon is 2x much faster than with XSuperObject.
type
TUser = class
private
FName: string;
FFlags: UInt32;
FFields: UInt32;
FCreated: TDateTime;
public
property Name: string read FName write FName;
property Flags: UInt32 read FFlags write FFlags;
published
property Fields: UInt32 read FFields write FFields;
property Created: TDateTime read FCreated write FCreated;
constructor Create;
end;
function MyToJson(User: TUser): string;
var
Config: INeonConfiguration;
LJSON: TJSONValue;
LWriter: TNeonSerializerJSON;
begin
Config := TNeonConfiguration.Default.SetVisibility([mvPublic{, mvPublished}]);
LWriter := TNeonSerializerJSON.Create(Config);
try
LJSON := LWriter.ObjectToJSON(User);
try
Result := TJSONUtils.ToJSON(LJSON);
finally
LJSON.Free;
end;
finally
LWriter.Free;
end;
end;
procedure MyFromJson(var User: TUser; const AJson: string);
var
Config: INeonConfiguration;
LJSON: TJSONValue;
LReader: TNeonDeserializerJSON;
begin
LJSON := TJSONObject.ParseJSONValue(AJson);
if not Assigned(LJSON) then
Exit;
Config := TNeonConfiguration.Default.SetVisibility([mvPublic{, mvPublished}]);
try
LReader := TNeonDeserializerJSON.Create(Config);
try
LReader.JSONToObject(User, LJSON);
finally
LReader.Free;
end;
finally
LJSON.Free;
end;
end;
Related
I have DataSnap Server and I have some server method to Set and Get object with variant fields
here is a n example of object I can access via DataSnap :
type
TOrder = class
private
[JSONReflect(ctObject, rtObject, TSampleVariantInterceptor, nil, true)]
FComment: Variant;
[JSONReflect(ctObject, rtObject, TSampleVariantInterceptor, nil, true)]
FNumber: Variant;
procedure SetComment(const Value: Variant);
procedure SetNumber(const Value: Variant);
public
property Number: Variant read FNumber write SetNumber;
property Comment: Variant read FComment write SetComment;
end;
implementation
{ TOrder }
procedure TOrder.SetComment(const Value: Variant);
begin
FComment := Value;
end;
procedure TOrder.SetNumber(const Value: Variant);
begin
FNumber := Value;
end;
here is my ServerMethod sample code :
TsmOrder = class(TDSServerModule)
public
...
function GetOrder(const AID: Integer): TOrder;
function SetOrder(const AOrder: TOrder): Integer;
end;
here is my unit for where is defined my TSampleVariantInterceptor
unit MarshallingUtils;
interface
uses SysUtils, Classes, DBXJSON, StrUtils, RTTI, DBXJSONReflect, Variants;
type
TSampleVariantInterceptor = class(TJSONInterceptor)
private
public
function ObjectConverter(Data: TObject; Field: String): TObject; override;
procedure ObjectReverter(Data: TObject; Field: String; Arg: TObject); override;
end;
[JSONReflect(true)]
TReflectVariantObject = class
private
FType: TVarType;
FValue: string;
public
constructor Create(ASampleVariant: Variant);
function GetVariant: Variant;
end;
implementation
const
NullVariantString = 'null';
{ TSampleVariantInterceptor }
function TSampleVariantInterceptor.ObjectConverter(Data: TObject;
Field: String): TObject;
var
LRttiContext: TRttiContext;
LVariant: Variant;
begin
LVariant := LRttiContext.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType<Variant>;
Result := TReflectVariantObject.Create(LVariant);
end;
procedure TSampleVariantInterceptor.ObjectReverter(Data: TObject; Field: String;
Arg: TObject);
var
LRttiContext: TRttiContext;
LRttiField: TRttiField;
LVariant: Variant;
begin
Assert(Arg is TReflectVariantObject);
LVariant := TReflectVariantObject(Arg).GetVariant;
LRttiField := LRttiContext.GetType(Data.ClassType).GetField(Field);
LRttiField.SetValue(Data, TValue.FromVariant(LVariant));
Arg.Free;
end;
{ TReflectVariantObject }
constructor TReflectVariantObject.Create(ASampleVariant: Variant);
begin
FType := VarType(ASampleVariant);
case FType of
varNull: FValue := NullVariantString;
else
FValue := ASampleVariant; // Convert to string
end;
end;
function TReflectVariantObject.GetVariant: Variant;
var
V: Variant;
begin
if FValue = NullVariantString then
V := Null
else
V := FValue;
VarCast(Result, V, FType);
end;
end.
My variant is well converted in my server : I can't see the null string in fiddler but in my client application my variant appear to Empty instead of Null Variant. Do I make something wrong ?
I am very much new in delphi. Presently I am facing a problem. i want to convert nested object into Json using TJson, but having memory related issue.
Here is my code.
It is just simple unit file with Person and Address class. The person class is depended on the address class.
unit uPerson;
interface
uses
REST.Json;
type
TAddress = class
private
FStreetNAme: string;
FState: string;
FPinCode: string;
published
property StreetNAme: string read FStreetNAme write FStreetNAme;
property State: string read FState write FState;
property PinCode: string read FPinCode write FPinCode;
end;
TPerson = class
private
FName: string;
FAge: Integer;
FSalary: Double;
[JSONMarshalled(True)]
FAddress: TAddress;
published
property Name: string read FName write FName;
property Age: Integer read FAge write FAge;
property Salary: Double read FSalary write FSalary;
property Address: TAddress read FAddress write FAddress;
end;
implementation
end.
Below is the main form code
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, uPerson,
REST.JSON;
type
TForm1 = class(TForm)
edtName: TLabeledEdit;
edtAge: TLabeledEdit;
edtSalary: TLabeledEdit;
edtStreet: TLabeledEdit;
edtState: TLabeledEdit;
edtPin: TLabeledEdit;
btnSave: TButton;
Memo1: TMemo;
procedure btnSaveClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Person: TPerson;
Add: TAddress;
implementation
{$R *.dfm}
procedure TForm1.btnSaveClick(Sender: TObject);
var
jsonString: string;
begin
Person := TPerson.Create;
try
Person.Name := edtName.Text;
Person.Age := Integer.Parse(edtAge.Text);
Person.Salary := double.Parse(edtSalary.Text);
Add.StreetNAme := edtStreet.Text;
Add.State := edtState.Text;
Add.PinCode := edtPin.Text;
Person.Address := Add;
jsonString := TJson.ObjectToJsonString(Person);
Memo1.Text := jsonString;
finally
Add.Free;
Person.Free;
end;
//
end;
end.
The code is compiling properly. But when try to generate the json it is giving access violation error. Here is the image - access violation error image
[Update]
Basically I am getting "access violation at 0x00409fca: write of address 0x00000004".
Thank you in advance.
I don't know anything about the JSON facilities, but I do know Delphi memory management.
And this error is expected, because you forget to create the TAddress object.
The first problem is this line:
Add.StreetNAme := edtStreet.Text;
Add is a global variable, so it initially is set to nil (since it is an object pointer). Consequently, you here try to write to a memory address very close to 0, which is precisely what you see in the exception message.
You need to create a TAddress object on the heap and assign the address of this object to the Add variable.
Just like you do for the TPerson object.
procedure TForm1.btnSaveClick(Sender: TObject);
var
jsonString: string;
begin
Person := TPerson.Create;
try
Add := TAddress.Create;
try
Person.Name := edtName.Text;
Person.Age := Integer.Parse(edtAge.Text);
Person.Salary := double.Parse(edtSalary.Text);
Add.StreetName := edtStreet.Text;
Add.State := edtState.Text;
Add.PinCode := edtPin.Text;
Person.Address := Add;
jsonString := TJson.ObjectToJsonString(Person);
Memo1.Text := jsonString;
finally
Add.Free;
end;
finally
Person.Free;
end;
end;
Also, it's not a good idea to use global variables here. Instead, use local variables. And there is no need for a separate TAddress variable at all:
var
Person: TPerson;
jsonString: string;
begin
Person := TPerson.Create;
try
Person.Address := TAddress.Create;
try
Person.Name := edtName.Text;
Person.Age := Integer.Parse(edtAge.Text);
Person.Salary := double.Parse(edtSalary.Text);
Person.Address.StreetName := edtStreet.Text;
Person.Address.State := edtState.Text;
Person.Address.PinCode := edtPin.Text;
jsonString := TJson.ObjectToJsonString(Person);
Memo1.Text := jsonString;
finally
Person.Address.Free;
end;
finally
Person.Free;
end;
end;
Furthermore, you might argue that it would be better if the TPerson constructor created a TAddress object and put a pointer to it in its Address field. Then the TPerson destructor would also be responsible for freeing this object:
unit uPerson;
interface
uses
REST.Json;
type
TAddress = class
private
FStreetNAme: string;
FState: string;
FPinCode: string;
published
property StreetNAme: string read FStreetNAme write FStreetNAme;
property State: string read FState write FState;
property PinCode: string read FPinCode write FPinCode;
end;
TPerson = class
private
FName: string;
FAge: Integer;
FSalary: Double;
[JSONMarshalled(True)]
FAddress: TAddress;
public
constructor Create;
destructor Destroy; override;
published
property Name: string read FName write FName;
property Age: Integer read FAge write FAge;
property Salary: Double read FSalary write FSalary;
property Address: TAddress read FAddress write FAddress;
end;
implementation
{ TPerson }
constructor TPerson.Create;
begin
FAddress := TAddress.Create;
end;
destructor TPerson.Destroy;
begin
FAddress.Free;
inherited;
end;
end.
and
var
Person: TPerson;
jsonString: string;
begin
Person := TPerson.Create;
try
Person.Name := 'Andreas';
Person.Age := 32;
Person.Salary := 12345;
Person.Address.StreetName := 'Street';
Person.Address.State := 'State';
Person.Address.PinCode := 'pin';
jsonString := TJson.ObjectToJsonString(Person);
Memo1.Text := jsonString;
finally
Person.Free;
end;
end;
How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.
How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.
How to get ALL 'id' member values from a generic JSON. Without knowing structure of it. Because its very complex and it has a lot of sub objects. It has to loop through all the sub objects.
Again for people that keep on asking where is the example JSON. My question is about how to extract a member value in my case "id" from any generic JSON that has this member inside.
If you don't know the structure of the JSON you receive from somewhere, it is important to note that JSON is "simply" a composite pattern and you can traverse it like any other composite structure. The following example traverse the complete structure in a JSON text and prints the path of any member named 'id'.
procedure ParseJSON;
var
JSONText: string;
JSON: ISuperObject;
begin
// Retrieve JSON as a string into JSONText variable any way you like.
JSON := SO(JSONText);
ProcessObject(JSON.AsObject);
end;
procedure ProcessObject(const aAsObject: TSuperTableString; const aPrefix: string = '');
var
Names: ISuperObject;
Name: string;
Items: ISuperObject;
Item: ISuperObject;
idx: Integer;
Value: string;
ArrayItem: ISuperObject;
begin
if Assigned(aAsObject) then
begin
Names := aAsObject.GetNames;
Items := aAsObject.GetValues;
for idx := 0 to Items.AsArray.Length - 1 do
begin
Name := Names.AsArray[idx].AsString;
Item := Items.AsArray[idx];
if Item.DataType = stObject then
Value := '<Object>'
else if Item.DataType = stArray then
Value := '<Array>'
else
Value := Item.AsString;
if SameText(Name, 'id') then
WriteLn(Format('%s: %s', [aPrefix + Name, Value]));
if Item.DataType = stArray then
for ArrayItem in Item do
ProcessObject(ArrayItem.AsObject, aPrefix + Name + '.');
if Item.DataType = stObject then
ProcessObject(Item.AsObject, aPrefix + Name + '.');
end;
end;
end;